mirror of
https://github.com/gnustep/libs-gdl2.git
synced 2025-02-21 02:20:55 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@30496 72102866-910b-0410-8b05-ffd578937521
7651 lines
234 KiB
Objective-C
7651 lines
234 KiB
Objective-C
/**
|
|
EODatabaseContext.m <title>EODatabaseContext Class</title>
|
|
|
|
Copyright (C) 2000-2002,2003,2004,2005 Free Software Foundation, Inc.
|
|
|
|
Author: Mirko Viviani <mirko.viviani@gmail.com>
|
|
Date: June 2000
|
|
|
|
Author: Manuel Guesdon <mguesdon@orange-concept.com>
|
|
Date: October 2000
|
|
|
|
$Revision$
|
|
$Date$
|
|
|
|
<abstract></abstract>
|
|
|
|
This file is part of the GNUstep Database Library.
|
|
|
|
<license>
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, write to the Free Software Foundation,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
</license>
|
|
**/
|
|
|
|
#include "config.h"
|
|
|
|
RCS_ID("$Id$")
|
|
|
|
#ifdef GNUSTEP
|
|
#include <Foundation/NSArray.h>
|
|
#include <Foundation/NSDictionary.h>
|
|
#include <Foundation/NSString.h>
|
|
#include <Foundation/NSException.h>
|
|
#include <Foundation/NSValue.h>
|
|
#include <Foundation/NSZone.h>
|
|
#include <Foundation/NSNotification.h>
|
|
#include <Foundation/NSSet.h>
|
|
#include <Foundation/NSData.h>
|
|
#include <Foundation/NSKeyValueCoding.h>
|
|
#include <Foundation/NSDebug.h>
|
|
#else
|
|
#include <Foundation/Foundation.h>
|
|
#endif
|
|
|
|
#ifndef GNUSTEP
|
|
#include <GNUstepBase/GNUstep.h>
|
|
#include <GNUstepBase/NSDebug+GNUstepBase.h>
|
|
#endif
|
|
|
|
#include <GNUstepBase/GSObjCRuntime.h>
|
|
|
|
#include <EOControl/EOFault.h>
|
|
#include <EOControl/EOEditingContext.h>
|
|
#include <EOControl/EOClassDescription.h>
|
|
#include <EOControl/EOGenericRecord.h>
|
|
#include <EOControl/EOQualifier.h>
|
|
#include <EOControl/EOKeyGlobalID.h>
|
|
#include <EOControl/EOFetchSpecification.h>
|
|
#include <EOControl/EOSortOrdering.h>
|
|
#include <EOControl/EOKeyValueCoding.h>
|
|
#include <EOControl/EOMutableKnownKeyDictionary.h>
|
|
#include <EOControl/EOCheapArray.h>
|
|
#include <EOControl/EONSAddOns.h>
|
|
#include <EOControl/EONull.h>
|
|
#include <EOControl/EODebug.h>
|
|
|
|
#include <EOAccess/EOAdaptor.h>
|
|
#include <EOAccess/EOAdaptorChannel.h>
|
|
#include <EOAccess/EOAdaptorContext.h>
|
|
#include <EOAccess/EOModel.h>
|
|
#include <EOAccess/EOModelGroup.h>
|
|
#include <EOAccess/EOEntity.h>
|
|
#include <EOAccess/EORelationship.h>
|
|
#include <EOAccess/EOAttribute.h>
|
|
#include <EOAccess/EOStoredProcedure.h>
|
|
#include <EOAccess/EOJoin.h>
|
|
|
|
#include <EOAccess/EODatabase.h>
|
|
#include <EOAccess/EODatabaseContext.h>
|
|
#include <EOAccess/EODatabaseChannel.h>
|
|
#include <EOAccess/EODatabaseOperation.h>
|
|
#include <EOAccess/EOAccessFault.h>
|
|
#include <EOAccess/EOExpressionArray.h>
|
|
|
|
#include "EOPrivate.h"
|
|
#include "EOEntityPriv.h"
|
|
#include "EOAccessFaultPriv.h"
|
|
#include "EODatabaseContextPriv.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#define _LOCK_BUFFER 128
|
|
|
|
|
|
NSString *EODatabaseChannelNeededNotification = @"EODatabaseChannelNeededNotification";
|
|
|
|
NSString *EODatabaseContextKey = @"EODatabaseContextKey";
|
|
NSString *EODatabaseOperationsKey = @"EODatabaseOperationsKey";
|
|
NSString *EOFailedDatabaseOperationKey = @"EOFailedDatabaseOperationKey";
|
|
|
|
NSString *EOCustomQueryExpressionHintKey = @"EOCustomQueryExpressionHintKey";
|
|
NSString *EOStoredProcedureNameHintKey = @"EOStoredProcedureNameHintKey";
|
|
|
|
@interface EODatabaseContext(EOObjectStoreSupportPrivate)
|
|
- (id) entityForGlobalID: (EOGlobalID *)globalID;
|
|
@end
|
|
|
|
@implementation EODatabaseContext
|
|
|
|
// Initializing instances
|
|
|
|
static Class _contextClass = Nil;
|
|
|
|
+ (void)initialize
|
|
{
|
|
static BOOL initialized=NO;
|
|
if (!initialized)
|
|
{
|
|
initialized=YES;
|
|
|
|
GDL2_EOAccessPrivateInit();
|
|
|
|
_contextClass = GDL2_EODatabaseContextClass;
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_registerDatabaseContext:)
|
|
name: EOCooperatingObjectStoreNeeded
|
|
object: nil];
|
|
}
|
|
}
|
|
|
|
+ (EODatabaseContext*)databaseContextWithDatabase: (EODatabase *)database
|
|
{
|
|
return AUTORELEASE([[self alloc] initWithDatabase: database]);
|
|
}
|
|
|
|
+ (void)_registerDatabaseContext:(NSNotification *)notification
|
|
{
|
|
EOObjectStoreCoordinator *coordinator = [notification object];
|
|
EODatabaseContext *dbContext = nil;
|
|
EOModel *model = nil;
|
|
NSString *entityName = nil;
|
|
id keyValue = nil;
|
|
|
|
keyValue = [[notification userInfo] objectForKey: @"globalID"];
|
|
|
|
if (keyValue == nil)
|
|
keyValue = [[notification userInfo] objectForKey: @"fetchSpecification"];
|
|
|
|
if (keyValue == nil)
|
|
keyValue = [[notification userInfo] objectForKey: @"object"];
|
|
|
|
if (keyValue)
|
|
entityName = [keyValue entityName];
|
|
|
|
if (entityName)
|
|
model = [[[EOModelGroup defaultGroup] entityNamed:entityName] model];
|
|
|
|
if (model == nil)
|
|
NSLog(@"%@ -- %@ 0x%x: No model for entity named %@",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self,
|
|
entityName);
|
|
|
|
dbContext = [EODatabaseContext databaseContextWithDatabase:
|
|
[EODatabase databaseWithModel: model]];
|
|
|
|
[coordinator addCooperatingObjectStore:dbContext];
|
|
}
|
|
|
|
- (void) registerForAdaptorContextNotifications: (EOAdaptorContext*)adaptorContext
|
|
{
|
|
//OK
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_beginTransaction)
|
|
name: EOAdaptorContextBeginTransactionNotification
|
|
object: adaptorContext];
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_commitTransaction)
|
|
name: EOAdaptorContextCommitTransactionNotification
|
|
object: adaptorContext];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_rollbackTransaction)
|
|
name: EOAdaptorContextRollbackTransactionNotification
|
|
object: adaptorContext];
|
|
}
|
|
|
|
- (id) initWithDatabase: (EODatabase *)database
|
|
{
|
|
//OK
|
|
|
|
|
|
|
|
|
|
if ((self = [self init]))
|
|
{
|
|
_adaptorContext = RETAIN([[database adaptor] createAdaptorContext]);
|
|
|
|
if (_adaptorContext == nil)
|
|
{
|
|
NSLog(@"EODatabaseContext could not create adaptor context");
|
|
AUTORELEASE(self);
|
|
|
|
return nil;
|
|
}
|
|
_database = RETAIN(database);
|
|
|
|
// Register this object into database
|
|
[_database registerContext: self];
|
|
[self setUpdateStrategy: EOUpdateWithOptimisticLocking];
|
|
|
|
_uniqueStack = [NSMutableArray new];
|
|
_deleteStack = [NSMutableArray new];
|
|
_uniqueArrayStack = [NSMutableArray new];
|
|
|
|
_registeredChannels = [NSMutableArray new];
|
|
_batchFaultBuffer = [NSMutableDictionary new];
|
|
_batchToManyFaultBuffer = [NSMutableDictionary new];
|
|
|
|
// We want to know when snapshots change in database
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_snapshotsChangedInDatabase:)
|
|
name: EOObjectsChangedInStoreNotification
|
|
object: _database];
|
|
|
|
// We want to know when objects change
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_objectsChanged:)
|
|
name: EOObjectsChangedInStoreNotification
|
|
object: self];
|
|
|
|
[self registerForAdaptorContextNotifications: _adaptorContext];
|
|
|
|
//???
|
|
/*NO _snapshots = [NSMutableDictionary new];
|
|
_toManySnapshots = [NSMutableDictionary new];
|
|
*/
|
|
|
|
//NO _lock = [NSRecursiveLock new];
|
|
|
|
|
|
/* //TODO ?
|
|
transactionStackTop = NULL;
|
|
transactionNestingLevel = 0;
|
|
isKeepingSnapshots = YES;
|
|
isUniquingObjects = [database uniquesObjects];
|
|
[database contextDidInit:self];*/
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)_snapshotsChangedInDatabase: (NSNotification *)notification
|
|
{
|
|
//OK EOObjectsChangedInStoreNotification EODatabase
|
|
|
|
|
|
if ([notification object] == _database)//??
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: [notification name]
|
|
object: self
|
|
userInfo: [notification userInfo]];//==> _objectsChanged
|
|
|
|
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
|
|
[_database unregisterContext: self];
|
|
|
|
DESTROY(_adaptorContext);
|
|
DESTROY(_database);
|
|
|
|
if (_dbOperationsByGlobalID)
|
|
{
|
|
NSDebugMLog(@"MEMORY: dbOperationsByGlobalID count=%u",
|
|
NSCountMapTable(_dbOperationsByGlobalID));
|
|
NSFreeMapTable(_dbOperationsByGlobalID);
|
|
_dbOperationsByGlobalID = NULL;
|
|
}
|
|
/*NO
|
|
DESTROY(_snapshots);
|
|
DESTROY(_toManySnapshots);
|
|
*/
|
|
DESTROY(_uniqueStack);
|
|
DESTROY(_deleteStack);
|
|
DESTROY(_uniqueArrayStack);
|
|
|
|
DESTROY(_registeredChannels);
|
|
|
|
DESTROY(_batchFaultBuffer);
|
|
DESTROY(_batchToManyFaultBuffer);
|
|
|
|
DESTROY(_lastEntity);
|
|
|
|
if (_nonPrimaryKeyGenerators)
|
|
{
|
|
NSDebugMLog(@"MEMORY: nonPrimaryKeyGnerators count=%u",
|
|
NSCountHashTable(_nonPrimaryKeyGenerators));
|
|
|
|
NSFreeHashTable(_nonPrimaryKeyGenerators);
|
|
_nonPrimaryKeyGenerators = NULL;
|
|
}
|
|
|
|
if (_lockedObjects)
|
|
{
|
|
NSResetHashTable(_lockedObjects);
|
|
}
|
|
|
|
DESTROY(_lock);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
+ (EODatabaseContext *)registeredDatabaseContextForModel: (EOModel *)model
|
|
editingContext: (EOEditingContext *)editingContext
|
|
{
|
|
EOObjectStoreCoordinator *edObjectStore;
|
|
NSArray *cooperatingObjectStores;
|
|
NSEnumerator *storeEnum;
|
|
EOCooperatingObjectStore *coObjectStore;
|
|
EODatabase *anDatabase;
|
|
NSArray *models;
|
|
EODatabaseContext *dbContext = nil;
|
|
|
|
|
|
|
|
if (model && editingContext)
|
|
{
|
|
IMP enumNO=NULL; // nextObject
|
|
edObjectStore = (EOObjectStoreCoordinator *)[editingContext rootObjectStore];
|
|
cooperatingObjectStores = [edObjectStore cooperatingObjectStores]; // get all EODatabaseContexts
|
|
|
|
storeEnum = [cooperatingObjectStores objectEnumerator];
|
|
|
|
while ((coObjectStore = GDL2_NextObjectWithImpPtr(storeEnum,&enumNO)))
|
|
{
|
|
if ([coObjectStore isKindOfClass: [EODatabaseContext class]])
|
|
{
|
|
anDatabase = [(EODatabaseContext *)coObjectStore database];
|
|
|
|
if (anDatabase && (models = [anDatabase models]))
|
|
{
|
|
if ([models containsObject: model])
|
|
{
|
|
dbContext = (EODatabaseContext *)coObjectStore;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dbContext)
|
|
{
|
|
// no EODatabaseContext found, create a new one
|
|
dbContext = [EODatabaseContext databaseContextWithDatabase:
|
|
[EODatabase databaseWithModel:
|
|
model]];
|
|
|
|
if (dbContext)
|
|
{
|
|
[edObjectStore addCooperatingObjectStore: dbContext];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return dbContext;
|
|
}
|
|
|
|
|
|
+ (Class)contextClassToRegister
|
|
{
|
|
NSEmitTODO();
|
|
// TODO;
|
|
return _contextClass;
|
|
}
|
|
|
|
+ (void)setContextClassToRegister: (Class)contextClass
|
|
{
|
|
_contextClass = contextClass;
|
|
}
|
|
|
|
/** Returns YES if we have at least one busy channel **/
|
|
- (BOOL)hasBusyChannels
|
|
{
|
|
BOOL busy = NO;
|
|
NSUInteger count = 0;
|
|
|
|
count = [_registeredChannels count];
|
|
|
|
if (count>0)
|
|
{
|
|
NSUInteger i = 0;
|
|
IMP oaiIMP=[_registeredChannels methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0 ; !busy && i < count; i++)
|
|
{
|
|
EODatabaseChannel *channel = [GDL2_ObjectAtIndexWithImp(_registeredChannels,oaiIMP,i)
|
|
nonretainedObjectValue];
|
|
|
|
busy = [channel isFetchInProgress];
|
|
}
|
|
};
|
|
|
|
return busy;
|
|
}
|
|
|
|
- (NSArray *)registeredChannels
|
|
{
|
|
NSMutableArray *array = nil;
|
|
NSUInteger i, count;
|
|
|
|
count = [_registeredChannels count];
|
|
array = [NSMutableArray arrayWithCapacity: count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[_registeredChannels methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
[array addObject: [GDL2_ObjectAtIndexWithImp(_registeredChannels,oaiIMP,i)
|
|
nonretainedObjectValue]];
|
|
};
|
|
|
|
return array;
|
|
}
|
|
|
|
- (void)registerChannel: (EODatabaseChannel *)channel
|
|
{
|
|
//call channel databaseContext
|
|
//test if not exists _registeredChannels indexOfObjectIdenticalTo:channel
|
|
NSDebugLog(@"** REGISTER channel ** debug:%d ** total registered:%d",
|
|
[[channel adaptorChannel] isDebugEnabled],
|
|
[_registeredChannels count] + 1);
|
|
|
|
[_registeredChannels addObject:
|
|
[NSValue valueWithNonretainedObject: channel]];
|
|
[channel setDelegate: nil];
|
|
}
|
|
|
|
- (void)unregisterChannel: (EODatabaseChannel *)channel
|
|
{
|
|
NSUInteger i;
|
|
NSUInteger count= [_registeredChannels count];
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[_registeredChannels methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = count - 1; i >= 0; i--)
|
|
{
|
|
if ([GDL2_ObjectAtIndexWithImp(_registeredChannels,oaiIMP,i)
|
|
nonretainedObjectValue] == channel)
|
|
{
|
|
[_registeredChannels removeObjectAtIndex: i];
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/** returns a non busy channel if any, nil otherwise **/
|
|
-(EODatabaseChannel *)_availableChannelFromRegisteredChannels
|
|
{
|
|
NSEnumerator *channelsEnum;
|
|
NSValue *channel = nil;
|
|
IMP enumNO=NULL; // nextObject
|
|
|
|
channelsEnum = [_registeredChannels objectEnumerator];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",@"REGISTERED CHANNELS nb=%d",
|
|
[_registeredChannels count]);
|
|
|
|
while ((channel = GDL2_NextObjectWithImpPtr(channelsEnum,&enumNO)))
|
|
{
|
|
if ([(EODatabaseChannel *)[channel nonretainedObjectValue]
|
|
isFetchInProgress] == NO)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",@"CHANNEL %p is not busy",
|
|
[channel nonretainedObjectValue]);
|
|
|
|
return [channel nonretainedObjectValue];
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",@"CHANNEL %p is busy",
|
|
[channel nonretainedObjectValue]);
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
/** return a non busy channel **/
|
|
- (EODatabaseChannel *)availableChannel
|
|
{
|
|
EODatabaseChannel *channel = nil;
|
|
NSUInteger num = 2;
|
|
|
|
while (!channel && num)
|
|
{
|
|
channel = [self _availableChannelFromRegisteredChannels];
|
|
|
|
if (!channel)
|
|
{
|
|
//If not channel and last try: send a EODatabaseChannelNeededNotification notification before this last try
|
|
if (--num)
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: EODatabaseChannelNeededNotification
|
|
object: self];
|
|
}
|
|
}
|
|
|
|
if ((!channel) && ([_registeredChannels count] < 1)) {
|
|
channel = [EODatabaseChannel databaseChannelWithDatabaseContext: self];
|
|
if (channel)
|
|
{
|
|
[self registerChannel:channel];
|
|
}
|
|
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
/** returns the database **/
|
|
- (EODatabase *)database
|
|
{
|
|
return _database;
|
|
}
|
|
|
|
/** returns the coordinator **/
|
|
- (EOObjectStoreCoordinator *)coordinator
|
|
{
|
|
return _coordinator;
|
|
}
|
|
|
|
/** returns the adaptor context **/
|
|
- (EOAdaptorContext *)adaptorContext
|
|
{
|
|
return _adaptorContext;
|
|
}
|
|
|
|
/** Set the update strategy to 'strategy'
|
|
May raise an exception if transaction has began or if you want pessimistic lock when there's already a snapshot recorded
|
|
**/
|
|
- (void)setUpdateStrategy: (EOUpdateStrategy)strategy
|
|
{
|
|
if (_flags.beganTransaction)
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ -- %@ 0x%x: transaction in progress",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self];
|
|
|
|
//Can't set pessimistic locking where there's already snapshosts !
|
|
if (strategy == EOUpdateWithPessimisticLocking
|
|
&& [[_database snapshots] count])
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ -- %@ 0x%x: can't set EOUpdateWithPessimisticLocking when receive's EODatabase already has snapshots",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self];
|
|
|
|
_updateStrategy = strategy;
|
|
}
|
|
|
|
/** Get the update strategy **/
|
|
- (EOUpdateStrategy)updateStrategy
|
|
{
|
|
return _updateStrategy;
|
|
}
|
|
|
|
/** Get the delegate **/
|
|
- (id)delegate
|
|
{
|
|
return _delegate;
|
|
}
|
|
|
|
/** Set the delegate **/
|
|
- (void)setDelegate:(id)delegate
|
|
{
|
|
NSEnumerator *channelsEnum = [_registeredChannels objectEnumerator];
|
|
EODatabaseChannel *channel = nil;
|
|
IMP enumNO=NULL; // nextObject
|
|
|
|
_delegate = delegate;
|
|
|
|
_delegateRespondsTo.willRunLoginPanelToOpenDatabaseChannel =
|
|
[delegate respondsToSelector: @selector(databaseContext:willRunLoginPanelToOpenDatabaseChannel:)];
|
|
_delegateRespondsTo.newPrimaryKey =
|
|
[delegate respondsToSelector: @selector(databaseContext:newPrimaryKeyForObject:entity:)];
|
|
_delegateRespondsTo.willPerformAdaptorOperations =
|
|
[delegate respondsToSelector: @selector(databaseContext:willPerformAdaptorOperations:adaptorChannel:)];
|
|
_delegateRespondsTo.shouldInvalidateObject =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldInvalidateObjectWithGlobalID:snapshot:)];
|
|
_delegateRespondsTo.willOrderAdaptorOperations =
|
|
[delegate respondsToSelector: @selector(databaseContext:willOrderAdaptorOperationsFromDatabaseOperations:)];
|
|
_delegateRespondsTo.shouldLockObject =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldLockObjectWithGlobalID:snapshot:)];
|
|
_delegateRespondsTo.shouldRaiseForLockFailure =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldRaiseExceptionForLockFailure:)];
|
|
_delegateRespondsTo.shouldFetchObjects =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldFetchObjectsWithFetchSpecification:editingContext:)];
|
|
_delegateRespondsTo.didFetchObjects =
|
|
[delegate respondsToSelector: @selector(databaseContext:didFetchObjects:fetchSpecification:editingContext:)];
|
|
_delegateRespondsTo.shouldFetchObjectFault =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldFetchObjectsWithFetchSpecification:editingContext:)];
|
|
_delegateRespondsTo.shouldFetchArrayFault =
|
|
[delegate respondsToSelector: @selector(databaseContext:shouldFetchArrayFault:)];
|
|
|
|
while ((channel = GDL2_NextObjectWithImpPtr(channelsEnum,&enumNO)))
|
|
[channel setDelegate: delegate];
|
|
}
|
|
|
|
- (void)handleDroppedConnection
|
|
{
|
|
NSUInteger i;
|
|
|
|
EOFLOGObjectFnStartOrCond2(@"DatabaseLevel", @"EODatabaseContext");
|
|
|
|
DESTROY(_adaptorContext);
|
|
|
|
for (i = [_registeredChannels count] - 1; i >= 0; i--)
|
|
{
|
|
RELEASE((EODatabaseChannel *)[[_registeredChannels objectAtIndex: i]
|
|
nonretainedObjectValue]);
|
|
}
|
|
|
|
DESTROY(_registeredChannels);
|
|
|
|
_adaptorContext = RETAIN([[[self database] adaptor] createAdaptorContext]);
|
|
_registeredChannels = [NSMutableArray new];
|
|
|
|
EOFLOGObjectFnStopOrCond2(@"DatabaseLevel", @"EODatabaseContext");
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EODatabaseContext (EOObjectStoreSupport)
|
|
|
|
/** Return a fault for row 'row' **/
|
|
- (id)faultForRawRow: (NSDictionary *)row
|
|
entityNamed: (NSString *)entityName
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
EOEntity *entity;
|
|
EOGlobalID *gid;
|
|
id object;
|
|
|
|
|
|
|
|
entity = [_database entityNamed: entityName];
|
|
gid = [entity globalIDForRow: row];
|
|
|
|
object = [self faultForGlobalID: gid
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"object=%p of class (%@)",
|
|
object, [object class]);
|
|
|
|
|
|
|
|
return object;
|
|
}
|
|
|
|
/** return entity corresponding to 'globalID' **/
|
|
- (id) entityForGlobalID: (EOGlobalID *)globalID
|
|
{
|
|
//OK
|
|
NSString *entityName;
|
|
EOEntity *entity;
|
|
|
|
DESTROY(_lastEntity);
|
|
|
|
entityName = [globalID entityName];
|
|
entity = [_database entityNamed: entityName];
|
|
|
|
ASSIGN(_lastEntity, entity);
|
|
|
|
return entity;
|
|
}
|
|
|
|
/** Make object a fault **/
|
|
- (void) _turnToFault: (id)object
|
|
gid: (EOGlobalID *)globalID
|
|
editingContext: (EOEditingContext *)context
|
|
isComplete: (BOOL)isComplete
|
|
{
|
|
//OK
|
|
EOAccessFaultHandler *handler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSAssert(globalID, @"No globalID");
|
|
NSAssert1([globalID isKindOfClass: [EOKeyGlobalID class]],
|
|
@"globalID is not a EOKeyGlobalID but a %@",
|
|
[globalID class]);
|
|
|
|
if ([(EOKeyGlobalID*)globalID areKeysAllNulls])
|
|
NSWarnLog(@"All key of globalID %p (%@) are nulls",
|
|
globalID,
|
|
globalID);
|
|
|
|
handler = [EOAccessFaultHandler
|
|
accessFaultHandlerWithGlobalID: (EOKeyGlobalID*)globalID
|
|
databaseContext: self
|
|
editingContext: context];
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"object->class_pointer=%p",
|
|
GSObjCClass(object));
|
|
|
|
[EOFault makeObjectIntoFault: object
|
|
withHandler: handler];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"object->class_pointer=%p",
|
|
GSObjCClass(object));
|
|
|
|
[self _addBatchForGlobalID: (EOKeyGlobalID*)globalID
|
|
fault: object];
|
|
|
|
|
|
//TODO: use isComplete
|
|
}
|
|
|
|
/** Get a fault for 'globalID' **/
|
|
- (id)faultForGlobalID: (EOGlobalID *)globalID
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
//Seems OK
|
|
EOClassDescription *classDescription = nil;
|
|
EOEntity *entity;
|
|
id object = nil;
|
|
BOOL isFinal;
|
|
|
|
|
|
|
|
|
|
|
|
isFinal = [(EOKeyGlobalID *)globalID isFinal];
|
|
entity = [self entityForGlobalID: globalID];
|
|
|
|
NSAssert(entity, @"no entity");
|
|
|
|
classDescription = [entity classDescriptionForInstances];
|
|
|
|
|
|
|
|
object = [classDescription createInstanceWithEditingContext: context
|
|
globalID: globalID
|
|
zone: NULL];
|
|
|
|
NSAssert1(object, @"No Object. classDescription=%@", classDescription);
|
|
/*mirko: NO
|
|
NSDictionary *pk;
|
|
NSEnumerator *pkEnum;
|
|
NSString *pkKey;
|
|
NSArray *classPropertyNames;
|
|
classPropertyNames = [entity classPropertyNames];
|
|
pk = [entity primaryKeyForGlobalID:(EOKeyGlobalID *)globalID];
|
|
pkEnum = [pk keyEnumerator];
|
|
while ((pkKey = [pkEnum nextObject]))
|
|
{
|
|
if ([classPropertyNames containsObject:pkKey] == YES)
|
|
[obj takeStoredValue:[pk objectForKey:pkKey]
|
|
forKey:pkKey];
|
|
}
|
|
*/
|
|
|
|
|
|
if ([(EOKeyGlobalID *)globalID areKeysAllNulls])
|
|
NSWarnLog(@"All key of globalID %p (%@) are nulls",
|
|
globalID,
|
|
globalID);
|
|
|
|
[self _turnToFault: object
|
|
gid: globalID
|
|
editingContext: context
|
|
isComplete: isFinal];//??
|
|
|
|
|
|
|
|
EOEditingContext_recordObjectGlobalIDWithImpPtr(context,NULL,object,globalID);
|
|
|
|
|
|
|
|
return object;
|
|
}
|
|
|
|
/** Get an array fault for globalID for relationshipName **/
|
|
- (NSArray *)arrayFaultWithSourceGlobalID: (EOGlobalID *)globalID
|
|
relationshipName: (NSString *)relationshipName
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
//Seems OK
|
|
NSArray *obj = nil;
|
|
|
|
if (![globalID isKindOfClass: [EOKeyGlobalID class]])
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ -- %@ The globalID %@ must be an EOKeyGlobalID to be able to construct a fault",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
globalID];
|
|
}
|
|
else
|
|
{
|
|
EOAccessArrayFaultHandler *handler = nil;
|
|
|
|
obj = [EOCheapCopyMutableArray array];
|
|
handler = [EOAccessArrayFaultHandler
|
|
accessArrayFaultHandlerWithSourceGlobalID:
|
|
(EOKeyGlobalID*)globalID
|
|
relationshipName: relationshipName
|
|
databaseContext: self
|
|
editingContext: context];
|
|
|
|
[EOFault makeObjectIntoFault: obj
|
|
withHandler: handler];
|
|
|
|
[self _addToManyBatchForSourceGlobalID: (EOKeyGlobalID *)globalID
|
|
relationshipName: relationshipName
|
|
fault: (EOFault*)obj];
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
- (void)initializeObject: (id)object
|
|
withGlobalID: (EOGlobalID *)globalID
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
//near OK
|
|
EOEntity *entity = nil;
|
|
|
|
|
|
|
|
if ([globalID isTemporary])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
if (![(EOKeyGlobalID *)globalID isFinal])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
//mirko:
|
|
if (_updateStrategy == EOUpdateWithPessimisticLocking)
|
|
[self registerLockedObjectWithGlobalID: globalID];
|
|
|
|
entity = [self entityForGlobalID: globalID];
|
|
|
|
/*Mirko:
|
|
if ([object respondsToSelector:@selector(entity)])
|
|
entity = [object entity];
|
|
else
|
|
entity = [_database entityNamed:[globalID entityName]];
|
|
*/
|
|
|
|
[self initializeObject: object
|
|
row: EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,globalID) //shound be _currentSnapshot
|
|
entity: entity
|
|
editingContext: context];
|
|
|
|
|
|
}
|
|
|
|
- (void) _objectsChanged: (NSNotification*)notification
|
|
{
|
|
|
|
|
|
/*object==self EOObjectsChangedInStoreNotification
|
|
userInfo = {
|
|
deleted = (List Of GlobalIDs);
|
|
inserted = (List Of GlobalIDs);
|
|
updated = (List Of GlobalIDs);
|
|
*/
|
|
|
|
if ([notification object] != self)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
//OK for update
|
|
//TODO-NOW for insert/delete
|
|
NSDictionary *userInfo = [notification userInfo];
|
|
NSArray *updatedObjects = [userInfo objectForKey: EOUpdatedKey];
|
|
//NSArray *insertedObjects = [userInfo objectForKey: EOInsertedKey];
|
|
//NSArray *deletedObjects = [userInfo objectForKey: EODeletedKey];
|
|
NSUInteger i, count = [updatedObjects count];
|
|
|
|
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[updatedObjects methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOKeyGlobalID *gid=GDL2_ObjectAtIndexWithImp(updatedObjects,oaiIMP,i);
|
|
NSString *entityName;
|
|
|
|
|
|
|
|
entityName = [gid entityName];
|
|
|
|
|
|
|
|
[_database invalidateResultCacheForEntityNamed: entityName];
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void) _snapshotsChangedInDatabase: (NSNotification*)notification
|
|
{
|
|
|
|
|
|
/*
|
|
userInfo = {
|
|
deleted = (List Of GlobalIDs);
|
|
inserted = (List Of GlobalIDs);
|
|
updated = (List Of GlobalIDs);
|
|
}}
|
|
*/
|
|
|
|
if ([notification object] != self)
|
|
{
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: EOObjectsChangedInStoreNotification
|
|
object: self
|
|
userInfo: [notification userInfo]];
|
|
//call _objectsChanged: and ObjectStoreCoordinator _objectsChangedInSubStore:
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (NSArray *)objectsForSourceGlobalID: (EOGlobalID *)globalID
|
|
relationshipName: (NSString *)name
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
//Near OK
|
|
NSArray *objects = nil;
|
|
id sourceObjectFault = nil;
|
|
id relationshipValue = nil;
|
|
NSArray *sourceSnapshot = nil;
|
|
NSUInteger sourceSnapshotCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
//First get the id from which we search the source object
|
|
sourceObjectFault = [context faultForGlobalID: globalID
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceObjectFault %p=%@",
|
|
sourceObjectFault, sourceObjectFault);
|
|
|
|
// Get the fault value from source object
|
|
|
|
|
|
relationshipValue = [sourceObjectFault storedValueForKey: name];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relationshipValue %p=%@",
|
|
relationshipValue, relationshipValue);
|
|
|
|
//Try to see if there is a snapshot for the source object
|
|
sourceSnapshot = [_database snapshotForSourceGlobalID: globalID
|
|
relationshipName: name];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceSnapshot %p (%@)=%@",
|
|
sourceSnapshot, [sourceSnapshot class],
|
|
sourceSnapshot);
|
|
|
|
sourceSnapshotCount = [sourceSnapshot count];
|
|
|
|
if (sourceSnapshotCount > 0)
|
|
{
|
|
EOGlobalID *snapGID = nil;
|
|
id snapFault = nil;
|
|
NSUInteger i;
|
|
IMP addObjectIMP=NULL;
|
|
IMP oaiIMP=NULL;
|
|
|
|
[EOFault clearFault: relationshipValue];
|
|
|
|
// Be carefull: Never call methodForSelector before clearing fault !
|
|
addObjectIMP=[relationshipValue methodForSelector:@selector(addObject:)];
|
|
oaiIMP=[sourceSnapshot methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
|
|
for (i = 0; i < sourceSnapshotCount; i++)
|
|
{
|
|
snapGID = GDL2_ObjectAtIndexWithImp(sourceSnapshot,oaiIMP,i);
|
|
|
|
|
|
|
|
snapFault = [context faultForGlobalID: snapGID
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapFault=%@",
|
|
snapFault);
|
|
|
|
GDL2_AddObjectWithImp(relationshipValue,addObjectIMP,snapFault);
|
|
}
|
|
|
|
objects = relationshipValue;
|
|
}
|
|
else
|
|
{
|
|
EOEntity *entity;
|
|
EORelationship *relationship;
|
|
NSUInteger maxBatch = 0;
|
|
BOOL isToManyToOne = NO;
|
|
EOEntity *destinationEntity = nil;
|
|
EOModel *destinationEntityModel = nil;
|
|
NSArray *models = nil;
|
|
EOQualifier *auxiliaryQualifier = nil;
|
|
NSDictionary *contextSourceSnapshot = nil;
|
|
id sourceObject = nil;
|
|
EORelationship *inverseRelationship = nil;
|
|
EOEntity *invRelEntity = nil;
|
|
NSArray *invRelEntityClassProperties = nil;
|
|
NSString *invRelName = nil;
|
|
EOQualifier *qualifier = nil;
|
|
EOFetchSpecification *fetchSpec = nil;
|
|
|
|
// Get the source object entity
|
|
entity = [self entityForGlobalID: globalID];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"entity name=%@",
|
|
[entity name]);
|
|
|
|
//Get the relationship named 'name'
|
|
relationship = [entity relationshipNamed: name];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relationship=%@",
|
|
relationship);
|
|
|
|
//Get the max number of fault to fetch
|
|
maxBatch = [relationship numberOfToManyFaultsToBatchFetch];
|
|
|
|
isToManyToOne = [relationship isToManyToOne];//NO
|
|
|
|
if (isToManyToOne)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO if isToManyToOne
|
|
}
|
|
|
|
//Get the fault entity (aka relationsip destination entity)
|
|
destinationEntity = [relationship destinationEntity];
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationEntity name=%@",
|
|
[destinationEntity name]);
|
|
|
|
//Get the destination entity model
|
|
destinationEntityModel = [destinationEntity model];
|
|
|
|
//and _database model to verify if the destinationEntityModel is in database models
|
|
models = [_database models];
|
|
|
|
if ([models indexOfObjectIdenticalTo: destinationEntityModel]
|
|
== NSNotFound)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO error
|
|
}
|
|
|
|
//Get the relationship qualifier if any
|
|
auxiliaryQualifier = [relationship auxiliaryQualifier];//nil
|
|
|
|
if (auxiliaryQualifier)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO if auxqualif
|
|
}
|
|
|
|
//??
|
|
contextSourceSnapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,globalID);
|
|
|
|
//NSEmitTODO();
|
|
//TODO Why first asking for faultForGlobalID and now asking objectForGlobalID ??
|
|
|
|
sourceObject = [context objectForGlobalID: globalID];
|
|
|
|
|
|
inverseRelationship = [relationship inverseRelationship];
|
|
NSDebugMLLog(@"EODatabaseContext", @"inverseRelationship=%@",
|
|
inverseRelationship);
|
|
|
|
if (!inverseRelationship)
|
|
{
|
|
NSEmitTODO();
|
|
//[self notImplemented: _cmd]; //TODO if !inverseRelationship
|
|
inverseRelationship = [relationship hiddenInverseRelationship];
|
|
//VERIFY (don't know if this is the good way)
|
|
}
|
|
|
|
invRelEntity = [inverseRelationship entity];
|
|
invRelEntityClassProperties = [invRelEntity classProperties];
|
|
invRelName = [inverseRelationship name];
|
|
|
|
|
|
|
|
|
|
qualifier = [EOKeyValueQualifier qualifierWithKey: invRelName
|
|
operatorSelector: @selector(isEqualTo:)
|
|
value: sourceObject];
|
|
|
|
|
|
|
|
fetchSpec = [EOFetchSpecification fetchSpecification];
|
|
|
|
[fetchSpec setQualifier: qualifier];
|
|
[fetchSpec setEntityName: [destinationEntity name]];
|
|
|
|
|
|
|
|
objects = [context objectsWithFetchSpecification: fetchSpec
|
|
editingContext: context];
|
|
|
|
[self _registerSnapshot: objects
|
|
forSourceGlobalID: globalID
|
|
relationshipName: name
|
|
editingContext: context];//OK
|
|
}
|
|
|
|
|
|
|
|
|
|
return objects;
|
|
}
|
|
|
|
- (void)_registerSnapshot: (NSArray*)snapshot
|
|
forSourceGlobalID: (EOGlobalID*)globalID
|
|
relationshipName: (NSString*)name
|
|
editingContext: (EOEditingContext*)context
|
|
{
|
|
//OK
|
|
NSArray *gids;
|
|
|
|
|
|
|
|
gids = [context resultsOfPerformingSelector: @selector(globalIDForObject:)
|
|
withEachObjectInArray: snapshot];
|
|
|
|
[_database recordSnapshot: gids
|
|
forSourceGlobalID: globalID
|
|
relationshipName: name];
|
|
|
|
|
|
}
|
|
|
|
- (void)refaultObject: object
|
|
withGlobalID: (EOGlobalID *)globalID
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
|
|
|
|
[EOObserverCenter suppressObserverNotification];
|
|
|
|
NS_DURING
|
|
{
|
|
[object clearProperties];//OK
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
[EOObserverCenter enableObserverNotification];
|
|
|
|
|
|
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
[EOObserverCenter enableObserverNotification];
|
|
|
|
if ([(EOKeyGlobalID *)globalID areKeysAllNulls])
|
|
NSWarnLog(@"All key of globalID %p (%@) are nulls",
|
|
globalID,
|
|
globalID);
|
|
|
|
[self _turnToFault: object
|
|
gid: globalID
|
|
editingContext: context
|
|
isComplete: YES]; //Why YES ?
|
|
|
|
[self forgetSnapshotForGlobalID:globalID];
|
|
|
|
|
|
}
|
|
|
|
- (void)saveChangesInEditingContext: (EOEditingContext *)context
|
|
{
|
|
//TODO: locks ?
|
|
NSException *exception = nil;
|
|
|
|
|
|
|
|
[self prepareForSaveWithCoordinator: nil
|
|
editingContext: context];
|
|
|
|
[self recordChangesInEditingContext];
|
|
|
|
NS_DURING
|
|
{
|
|
[self performChanges];
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSDebugMLog(@"EXCEPTION: %@", localException);
|
|
exception = localException;
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
//I don't know if this is really the good place to catch exception and rollback...
|
|
if (exception)
|
|
{
|
|
[self rollbackChanges];
|
|
[exception raise];
|
|
}
|
|
else
|
|
[self commitChanges];
|
|
|
|
|
|
}
|
|
|
|
- (void)_fetchRelationship: (EORelationship *)relationship
|
|
withObjects: (NSArray *)objsArray
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
NSMutableArray *qualArray = nil;
|
|
NSEnumerator *objEnum = nil;
|
|
NSEnumerator *relEnum = nil;
|
|
NSDictionary *snapshot = nil;
|
|
id obj = nil;
|
|
id relObj = nil;
|
|
|
|
|
|
|
|
if ([objsArray count] > 0)
|
|
{
|
|
IMP globalIDForObjectIMP=NULL;
|
|
IMP enumNO=NULL; // nextObject
|
|
|
|
qualArray = [NSMutableArray arrayWithCapacity: 5];
|
|
|
|
if ([relationship isFlattened] == YES)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationship %@ isFlattened", relationship);
|
|
|
|
relEnum = [[relationship componentRelationships] objectEnumerator];
|
|
enumNO=NULL;
|
|
while ((relationship = GDL2_NextObjectWithImpPtr(relEnum,&enumNO)))
|
|
{
|
|
// TODO rebuild object array for relationship path
|
|
|
|
[self _fetchRelationship: relationship
|
|
withObjects: objsArray
|
|
editingContext: context];
|
|
}
|
|
}
|
|
|
|
objEnum = [objsArray objectEnumerator];
|
|
enumNO=NULL;
|
|
while ((obj = GDL2_NextObjectWithImpPtr(objEnum,&enumNO)))
|
|
{
|
|
EOGlobalID* gid=nil;
|
|
relObj = [obj storedValueForKey: [relationship name]];
|
|
gid = EOEditingContext_globalIDForObjectWithImpPtr(context,&globalIDForObjectIMP,relObj);
|
|
snapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid);
|
|
|
|
[qualArray addObject: [relationship
|
|
qualifierWithSourceRow: snapshot]];
|
|
}
|
|
|
|
[self objectsWithFetchSpecification:
|
|
[EOFetchSpecification
|
|
fetchSpecificationWithEntityName:
|
|
[[relationship destinationEntity] name]
|
|
qualifier: [EOAndQualifier qualifierWithQualifierArray:
|
|
qualArray]
|
|
sortOrderings: nil]
|
|
editingContext: context];
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (NSArray*) _fetchRawRowKeyPaths:(NSArray *) rawRowKeyPaths
|
|
fetchSpecification: (EOFetchSpecification*) fetchSpecification
|
|
entity: (EOEntity *) entity
|
|
editingContext: (EOEditingContext *) context
|
|
{
|
|
EOAdaptorChannel * adaptorChannel = [[self availableChannel] adaptorChannel];
|
|
NSMutableArray * results = [NSMutableArray array];
|
|
NSUInteger fetchLimit = 0;
|
|
NSUInteger rowsFetched = 0;
|
|
NSUInteger keyCount = [rawRowKeyPaths count];
|
|
id messageHandler = nil; // used to prompt the user after the fetch limit is reached.
|
|
NSString * hintKey = nil;
|
|
BOOL continueFetch = NO;
|
|
NSUInteger k;
|
|
|
|
NSArray * attributesToFetch;
|
|
if (keyCount == 0)
|
|
{
|
|
attributesToFetch = [entity attributesToFetch];
|
|
} else {
|
|
// Populate an array with the attributes we need
|
|
attributesToFetch = [NSMutableArray arrayWithCapacity:keyCount];
|
|
BOOL hasNonFlattenedAttributes = NO;
|
|
|
|
for (k = 0; k < keyCount; k++)
|
|
{
|
|
NSString * keyName = [rawRowKeyPaths objectAtIndex:k];
|
|
EOAttribute * attr = [entity attributeNamed:keyName];
|
|
if (!attr)
|
|
{
|
|
attr = [EOAttribute attributeWithParent:entity
|
|
definition:keyName];
|
|
|
|
} else {
|
|
if ((!hasNonFlattenedAttributes) && (![attr isFlattened]))
|
|
{
|
|
hasNonFlattenedAttributes = YES;
|
|
}
|
|
}
|
|
[attributesToFetch addObject:attr];
|
|
}
|
|
|
|
if (!hasNonFlattenedAttributes)
|
|
{
|
|
// check if lastObject is enouth.
|
|
// the reference however does only checks the lastObject.
|
|
|
|
EOAttribute * attr = [attributesToFetch lastObject];
|
|
EORelationship * relationship;
|
|
|
|
if ([attr isFlattened])
|
|
{
|
|
relationship = [[attr _definitionArray] objectAtIndex:0];
|
|
} else {
|
|
NSString * s1 = [rawRowKeyPaths lastObject];
|
|
NSString * relName = [[s1 componentsSeparatedByString:@"."] objectAtIndex:0];
|
|
relationship = [entity relationshipNamed:relName];
|
|
|
|
if ([relationship isFlattened])
|
|
{
|
|
relationship = [[relationship _definitionArray] objectAtIndex:0];
|
|
}
|
|
}
|
|
|
|
EOJoin * join = [[relationship joins] lastObject];
|
|
EOAttribute * attr2 = [join sourceAttribute];
|
|
|
|
[attributesToFetch addObject:attr2];
|
|
}
|
|
// our channel does not support this.
|
|
//[adaptorChannel _setRawDictionaryInitializerForAttributes:attributesToFetch];
|
|
}
|
|
if ((hintKey = [[fetchSpecification hints] objectForKey:@"EOCustomQueryExpressionHintKey"]))
|
|
{
|
|
if ([hintKey isKindOfClass:[NSString class]])
|
|
{
|
|
hintKey = [[[_adaptorContext adaptor] expressionClass] expressionForString:hintKey];
|
|
} else {
|
|
NSLog(@"%s - %@ is not an NSString but a %@",__PRETTY_FUNCTION__, hintKey, NSStringFromClass([hintKey class]));
|
|
}
|
|
} else {
|
|
EOQualifier * qualifier = [[fetchSpecification qualifier] schemaBasedQualifierWithRootEntity:entity];
|
|
|
|
if (qualifier != [fetchSpecification qualifier])
|
|
{
|
|
[fetchSpecification setQualifier:qualifier];
|
|
}
|
|
}
|
|
if (![adaptorChannel isOpen])
|
|
{
|
|
[adaptorChannel openChannel];
|
|
}
|
|
if (hintKey)
|
|
{
|
|
[adaptorChannel evaluateExpression:hintKey];
|
|
[adaptorChannel setAttributesToFetch:attributesToFetch];
|
|
} else {
|
|
[adaptorChannel selectAttributes:attributesToFetch
|
|
fetchSpecification:fetchSpecification
|
|
lock:NO
|
|
entity:entity];
|
|
}
|
|
|
|
// 0 is no fetch limit
|
|
fetchLimit = [fetchSpecification fetchLimit];
|
|
// TODO: check if we need to check for protocol EOMessageHandlers
|
|
if (([fetchSpecification promptsAfterFetchLimit]) && ([context messageHandler]))
|
|
{
|
|
messageHandler = [context messageHandler];
|
|
}
|
|
|
|
|
|
do {
|
|
do {
|
|
NSMutableDictionary * dict = [adaptorChannel fetchRowWithZone:NULL];
|
|
if (!dict) {
|
|
break;
|
|
}
|
|
[results addObject:dict];
|
|
rowsFetched++;
|
|
} while ((fetchLimit == 0) || (rowsFetched < fetchLimit));
|
|
|
|
if (!messageHandler) {
|
|
break;
|
|
}
|
|
|
|
continueFetch = [messageHandler editingContext:context
|
|
shouldContinueFetchingWithCurrentObjectCount:rowsFetched
|
|
originalLimit:fetchLimit
|
|
objectStore:self];
|
|
|
|
} while (continueFetch);
|
|
|
|
[adaptorChannel cancelFetch];
|
|
|
|
if (_delegate)
|
|
{
|
|
|
|
[_delegate databaseContext: self
|
|
didFetchObjects: results
|
|
fetchSpecification: fetchSpecification
|
|
editingContext: context];
|
|
}
|
|
return results;
|
|
}
|
|
|
|
- (NSArray *)objectsWithFetchSpecification: (EOFetchSpecification *)fetchSpecification
|
|
editingContext: (EOEditingContext *)context
|
|
{ // TODO
|
|
EODatabaseChannel *channel = nil;
|
|
NSMutableArray *array = nil;
|
|
NSDictionary *snapshot = nil;
|
|
NSString *entityName = nil;
|
|
EOEntity *entity = nil;
|
|
NSString *relationshipKeyPath = nil;
|
|
NSEnumerator *relationshipKeyPathEnum = nil;
|
|
NSMutableArray *qualArray = nil;
|
|
|
|
/*NSEnumerator *subEntitiesEnum = nil;
|
|
EOEntity *subEntity = nil;
|
|
NSArray *subEntities = nil;*/
|
|
NSArray* rawRowKeyPaths = nil;
|
|
BOOL usesDistinct = NO;
|
|
NSUInteger num = 0;
|
|
NSUInteger limit=0;
|
|
id obj = nil;
|
|
|
|
|
|
|
|
|
|
|
|
#warning fix this method! -- dw
|
|
channel = [self _obtainOpenChannel];
|
|
|
|
if (_flags.beganTransaction == NO)
|
|
{
|
|
[_adaptorContext beginTransaction];
|
|
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
|
|
if (_delegateRespondsTo.shouldFetchObjects == YES)
|
|
{
|
|
array = (id)[_delegate databaseContext: self
|
|
shouldFetchObjectsWithFetchSpecification: fetchSpecification
|
|
editingContext: context];
|
|
}
|
|
|
|
if (!array)
|
|
{
|
|
IMP enumNO=NULL; // nextObject
|
|
array = [NSMutableArray arrayWithCapacity: 8];
|
|
|
|
entityName = [fetchSpecification entityName];//OK
|
|
entity = [_database entityNamed: entityName];//OK
|
|
NSAssert1(entity,@"No entity named %@",
|
|
entityName);
|
|
|
|
/* moved in EODatabaseChannel _selectWithFetchSpecification:(EOFetchSpecification *)fetchSpecification
|
|
editingContext:(EOEditingContext *)context
|
|
|
|
limit = [fetchSpecification fetchLimit];
|
|
usesDistinct = [fetchSpecification usesDistinct];
|
|
|
|
|
|
subEntities = [entity subEntities];
|
|
|
|
if ([subEntities count] && [fetchSpecification isDeep] == YES)
|
|
{
|
|
subEntitiesEnum = [subEntities objectEnumerator];
|
|
while ((subEntity = [subEntitiesEnum nextObject]))
|
|
{
|
|
EOFetchSpecification *fetchSubEntity;
|
|
|
|
fetchSubEntity = AUTORELEASE([fetchSpecification copy]);
|
|
[fetchSubEntity setEntityName:[entity name]];
|
|
|
|
[array addObjectsFromArray:[context objectsWithFetchSpecification:
|
|
fetchSubEntity]];
|
|
}
|
|
}
|
|
*/
|
|
rawRowKeyPaths = [fetchSpecification rawRowKeyPaths];//OK
|
|
if (rawRowKeyPaths)
|
|
{
|
|
NSArray * rawRows = [self _fetchRawRowKeyPaths:rawRowKeyPaths
|
|
fetchSpecification:fetchSpecification
|
|
entity:entity
|
|
editingContext:context];
|
|
return rawRows;
|
|
} else {
|
|
// (stephane@sente.ch) Adapted implementation of non raw rows
|
|
|
|
//cachesObject
|
|
//fetchspe isDeep ret 1
|
|
|
|
if (!channel)
|
|
{
|
|
channel = [self _obtainOpenChannel];
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"channel class %@ [channel isFetchInProgress]=%s",
|
|
[channel class],
|
|
([channel isFetchInProgress] ? "YES" : "NO"));
|
|
|
|
//mirko:
|
|
//#if 0
|
|
// if (_flags.beganTransaction == NO
|
|
// && _updateStrategy == EOUpdateWithPessimisticLocking)
|
|
// {
|
|
// [_adaptorContext beginTransaction];
|
|
//
|
|
// NSDebugMLLog(@"EODatabaseContext",
|
|
// @"BEGAN TRANSACTION FLAG==>YES");
|
|
// _flags.beganTransaction = YES;
|
|
// }
|
|
//#endif
|
|
|
|
if ([entity isAbstractEntity] == NO) //Mirko ???
|
|
// (stephane@sente) Should we test deepInheritanceFetch?
|
|
{
|
|
int autoreleaseSteps = 20;
|
|
int autoreleaseStep = autoreleaseSteps;
|
|
BOOL promptsAfterFetchLimit = NO;
|
|
NSAutoreleasePool *arp = nil;//To avoid too much memory use when fetching a lot of objects
|
|
int limit = 0;
|
|
|
|
[channel selectObjectsWithFetchSpecification: fetchSpecification
|
|
editingContext: context];//OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress] ? "YES" : "NO"));
|
|
|
|
limit = [fetchSpecification fetchLimit];//OK
|
|
promptsAfterFetchLimit = [fetchSpecification promptsAfterFetchLimit];
|
|
|
|
|
|
|
|
NS_DURING
|
|
{
|
|
IMP channelFetchObjectIMP=
|
|
[channel methodForSelector:@selector(fetchObject)];
|
|
|
|
IMP arrayAddObjectIMP=
|
|
[array methodForSelector:@selector(addObject:)];
|
|
|
|
GDL2IMP_UINT arrayIndexOfObjectIdenticalToIMP=
|
|
(GDL2IMP_UINT)[array methodForSelector:@selector(indexOfObjectIdenticalTo:)];
|
|
|
|
arp = GDL2_NSAutoreleasePool_new();
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress] ? "YES" : "NO"));
|
|
|
|
while ((obj = (*channelFetchObjectIMP)(channel,@selector(fetchObject))))
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"fetched an object");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"FETCH OBJECT object=%@\n", obj);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"%d usesDistinct: %s", num,
|
|
(usesDistinct ? "YES" : "NO"));
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%@\n\n", obj);
|
|
|
|
if (usesDistinct == YES
|
|
&& num > 0
|
|
&& GDL2_IndexOfObjectIdenticalToWithImp(array,arrayIndexOfObjectIdenticalToIMP,obj)!=NSNotFound)
|
|
// (stephane@sente) I thought that DISTINCT was done on server-side?!?
|
|
{
|
|
obj = nil;
|
|
}
|
|
else
|
|
{
|
|
|
|
GDL2_AddObjectWithImp(array,arrayAddObjectIMP,obj);
|
|
NSDebugMLLog(@"EODatabaseContext", @"array count=%d",
|
|
[array count]);
|
|
num++;
|
|
|
|
if (limit > 0 && num >= limit)
|
|
{
|
|
if ([[context messageHandler]
|
|
editingContext: context
|
|
shouldContinueFetchingWithCurrentObjectCount: num
|
|
originalLimit: limit
|
|
objectStore: self] == YES)
|
|
limit = 0;//??
|
|
else
|
|
{
|
|
DESTROY(arp);
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
if (autoreleaseStep <= 0)
|
|
{
|
|
DESTROY(arp);
|
|
autoreleaseStep = autoreleaseSteps;
|
|
arp = GDL2_NSAutoreleasePool_new();
|
|
}
|
|
else
|
|
autoreleaseStep--;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"WILL FETCH NEXT OBJECT");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress]
|
|
? "YES" : "NO"));
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"finished fetch");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"array=%@", array);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"step 0 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
|
|
[channel cancelFetch]; //OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"step 1 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
|
|
|
|
//TODO
|
|
/*
|
|
handle exceptio in fetchObject
|
|
channel fetchObject
|
|
|
|
if eception:
|
|
if ([editcontext handleError:localException])
|
|
{
|
|
//TODO
|
|
}
|
|
else
|
|
{
|
|
//TODO
|
|
};
|
|
*/
|
|
DESTROY(arp);
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext", @"AN EXCEPTION: %@",
|
|
localException);
|
|
|
|
RETAIN(localException);
|
|
DESTROY(arp);
|
|
AUTORELEASE(localException);
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ([entity cachesObjects] == YES)//OK
|
|
{
|
|
///TODO MG!!!
|
|
NSMutableArray *cache;
|
|
EOQualifier *qualifier;
|
|
EOGlobalID *gid;
|
|
BOOL isFault;
|
|
|
|
qualifier = [fetchSpecification qualifier];
|
|
|
|
cache = (id)[_database resultCacheForEntityNamed: entityName];
|
|
if (cache == nil)
|
|
{
|
|
NSMutableDictionary *row = nil;
|
|
EOAdaptorChannel *adaptorChannel = nil;
|
|
|
|
IMP channelFetchRowWithZoneIMP=NULL;
|
|
IMP entityGlobalIDForRowIMP=[entity methodForSelector:@selector(globalIDForRow:)];
|
|
IMP databaseRecordSnapshotForGlobalID=[_database methodForSelector:@selector(recordSnapshot:forGlobalID:)];
|
|
IMP cacheAddObjectIMP=NULL;
|
|
|
|
channel = [self availableChannel];
|
|
adaptorChannel = [channel adaptorChannel];
|
|
|
|
channelFetchRowWithZoneIMP=
|
|
[adaptorChannel methodForSelector:@selector(fetchRowWithZone:)];
|
|
|
|
if (_flags.beganTransaction == NO && _updateStrategy == EOUpdateWithPessimisticLocking)
|
|
{
|
|
[_adaptorContext beginTransaction];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>YES");
|
|
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
|
|
[adaptorChannel selectAttributes: [entity attributesToFetch]
|
|
fetchSpecification:
|
|
[EOFetchSpecification
|
|
fetchSpecificationWithEntityName: entityName
|
|
qualifier: nil
|
|
sortOrderings: nil]
|
|
lock: NO
|
|
entity: entity];
|
|
|
|
cache = [NSMutableArray arrayWithCapacity: 16];
|
|
cacheAddObjectIMP=[cache methodForSelector:@selector(addObject:)];
|
|
|
|
while ((row = (*channelFetchRowWithZoneIMP)
|
|
(adaptorChannel,@selector(fetchRowWithZone:),NULL)))
|
|
{
|
|
|
|
|
|
gid = (*entityGlobalIDForRowIMP)(entity,@selector(globalIDForRow:),row);
|
|
|
|
|
|
(*databaseRecordSnapshotForGlobalID)
|
|
(_database,@selector(recordSnapshot:forGlobalID:),row,gid);
|
|
GDL2_AddObjectWithImp(cache,cacheAddObjectIMP,gid);
|
|
}
|
|
|
|
|
|
[channel cancelFetch];
|
|
|
|
|
|
[_database setResultCache: cache
|
|
forEntityNamed: entityName];
|
|
}
|
|
|
|
if ([cache count]>0)
|
|
{
|
|
IMP arrayAddObjectIMP=
|
|
[array methodForSelector:@selector(addObject:)];
|
|
|
|
GDL2IMP_UINT arrayIndexOfObjectIdenticalToIMP=
|
|
(GDL2IMP_UINT)[array methodForSelector:@selector(indexOfObjectIdenticalTo:)];
|
|
|
|
NSEnumerator *cacheEnum = [cache objectEnumerator];
|
|
|
|
IMP cacheEnumNextObjectIMP=[cacheEnum methodForSelector:@selector(nextObject)];
|
|
|
|
EOClassDescription* classDescriptionForInstances=
|
|
[entity classDescriptionForInstances];
|
|
|
|
GDL2IMP_BOOL qualifierEvaluateWithObjectIMP=
|
|
(GDL2IMP_BOOL)[qualifier methodForSelector:@selector(evaluateWithObject:)];
|
|
|
|
IMP ecObjectForGlobalIDIMP=NULL;
|
|
IMP ecRecordObjectGlobalIDIMP=NULL;
|
|
|
|
while ((gid = GDL2_NextObjectWithImp(cacheEnum,cacheEnumNextObjectIMP)))
|
|
{
|
|
|
|
|
|
snapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid);
|
|
if (snapshot)
|
|
{
|
|
if (!qualifier
|
|
|| (*qualifierEvaluateWithObjectIMP)(qualifier,@selector(evaluateWithObject:),snapshot) == YES)
|
|
{
|
|
obj = EOEditingContext_objectForGlobalIDWithImpPtr(context,&ecObjectForGlobalIDIMP,gid);
|
|
|
|
isFault = _isFault(obj);
|
|
|
|
if (obj == nil || isFault == YES)
|
|
{
|
|
if (isFault == NO)
|
|
{
|
|
obj = [classDescriptionForInstances
|
|
createInstanceWithEditingContext: context
|
|
globalID: gid
|
|
zone: NULL];
|
|
|
|
NSAssert1(obj, @"No Object. [entity classDescriptionForInstances]=%@", [entity classDescriptionForInstances]);
|
|
EOEditingContext_recordObjectGlobalIDWithImpPtr(context,&ecRecordObjectGlobalIDIMP,obj,gid);
|
|
}
|
|
else
|
|
{
|
|
[self _removeBatchForGlobalID:
|
|
(EOKeyGlobalID *)gid
|
|
fault: obj];
|
|
|
|
[EOFault clearFault: obj];
|
|
}
|
|
|
|
[context initializeObject: obj
|
|
withGlobalID: gid
|
|
editingContext: context];
|
|
|
|
[obj awakeFromFetchInEditingContext: context];
|
|
}
|
|
|
|
if (usesDistinct == YES
|
|
&& num > 0
|
|
&& GDL2_IndexOfObjectIdenticalToWithImp(array,arrayIndexOfObjectIdenticalToIMP,obj)!=NSNotFound)
|
|
{
|
|
obj = nil;
|
|
}
|
|
else
|
|
{
|
|
GDL2_AddObjectWithImp(array,arrayAddObjectIMP,obj);
|
|
num++;
|
|
|
|
if (limit && num >= limit)
|
|
{
|
|
if ([[context messageHandler]
|
|
editingContext: context
|
|
shouldContinueFetchingWithCurrentObjectCount: num
|
|
originalLimit: limit
|
|
objectStore: self] == YES)
|
|
limit = 0;
|
|
else
|
|
break;
|
|
}
|
|
};
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ([fetchSpecification sortOrderings])
|
|
array = (id)[array sortedArrayUsingKeyOrderArray:
|
|
[fetchSpecification sortOrderings]];
|
|
}
|
|
else
|
|
{
|
|
//cachesObject
|
|
//fetchspe isDeep ret 1
|
|
channel = [self _obtainOpenChannel];
|
|
|
|
if (!channel)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd];//TODO
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"channel class %@ [channel isFetchInProgress]=%s",
|
|
[channel class],
|
|
([channel isFetchInProgress] ? "YES" : "NO"));
|
|
|
|
//mirko:
|
|
if (_flags.beganTransaction == NO
|
|
&& _updateStrategy == EOUpdateWithPessimisticLocking)
|
|
{
|
|
[_adaptorContext beginTransaction];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>YES");
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
|
|
if ([entity isAbstractEntity] == NO) //Mirko ???
|
|
{
|
|
NSUInteger autoreleaseSteps = 20;
|
|
NSUInteger autoreleaseStep = autoreleaseSteps;
|
|
BOOL promptsAfterFetchLimit = NO;
|
|
NSAutoreleasePool *arp = nil;//To avoid too much memory use when fetching a lot of objects
|
|
NSUInteger limit = 0;
|
|
|
|
[channel selectObjectsWithFetchSpecification: fetchSpecification
|
|
editingContext: context];//OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress]
|
|
? "YES" : "NO"));
|
|
|
|
limit = [fetchSpecification fetchLimit];//OK
|
|
promptsAfterFetchLimit = [fetchSpecification promptsAfterFetchLimit];
|
|
|
|
|
|
|
|
NS_DURING
|
|
{
|
|
IMP channelFetchObjectIMP=
|
|
[channel methodForSelector:@selector(fetchObject)];
|
|
|
|
IMP arrayAddObjectIMP=
|
|
[array methodForSelector:@selector(addObject:)];
|
|
|
|
GDL2IMP_UINT arrayIndexOfObjectIdenticalToIMP=
|
|
(GDL2IMP_UINT)[array methodForSelector:@selector(indexOfObjectIdenticalTo:)];
|
|
|
|
arp = GDL2_NSAutoreleasePool_new();
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress]
|
|
? "YES" : "NO"));
|
|
|
|
while ((obj = (*channelFetchObjectIMP)(channel,@selector(fetchObject))))
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"fetched an object");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"FETCH OBJECT object=%@\n", obj);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"%d usesDistinct: %s",
|
|
num,
|
|
(usesDistinct ? "YES" : "NO"));
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%@\n\n", obj);
|
|
|
|
if (usesDistinct == YES
|
|
&& num > 0
|
|
&& GDL2_IndexOfObjectIdenticalToWithImp(array,arrayIndexOfObjectIdenticalToIMP,obj)!=NSNotFound)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Already fetched object=%@\n\n", obj);
|
|
obj = nil;
|
|
}
|
|
else
|
|
{
|
|
|
|
NSAssert(obj,@"No object");
|
|
GDL2_AddObjectWithImp(array,arrayAddObjectIMP,obj);
|
|
NSDebugMLLog(@"EODatabaseContext", @"array count=%d",
|
|
[array count]);
|
|
num++;
|
|
|
|
if (limit > 0 && num >= limit)
|
|
{
|
|
if ([[context messageHandler]
|
|
editingContext: context
|
|
shouldContinueFetchingWithCurrentObjectCount: num
|
|
originalLimit: limit
|
|
objectStore: self] == YES)
|
|
limit = 0;//??
|
|
else
|
|
break;
|
|
}
|
|
};
|
|
if (autoreleaseStep <= 0)
|
|
{
|
|
DESTROY(arp);
|
|
autoreleaseStep = autoreleaseSteps;
|
|
arp = GDL2_NSAutoreleasePool_new();
|
|
}
|
|
else
|
|
autoreleaseStep--;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"WILL FETCH NEXT OBJECT");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"[channel isFetchInProgress]=%s",
|
|
([channel isFetchInProgress]
|
|
? "YES" : "NO"));
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"finished fetch");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"array=%@", array);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"step 0 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
|
|
[channel cancelFetch]; //OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"step 1 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
|
|
|
|
//TODO
|
|
/*
|
|
handle exceptio in fetchObject
|
|
channel fetchObject
|
|
|
|
if eception:
|
|
if ([editcontext handleError:localException])
|
|
{
|
|
//TODO
|
|
}
|
|
else
|
|
{
|
|
//TODO
|
|
};
|
|
*/
|
|
|
|
DESTROY(arp);
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext", @"AN EXCEPTION: %@",
|
|
localException);
|
|
|
|
RETAIN(localException);
|
|
DESTROY(arp);
|
|
AUTORELEASE(localException);
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
}
|
|
}
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"step 2 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
}
|
|
|
|
//VERIFY
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"array before prefetchingRelationshipKeyPaths: %@", array);
|
|
|
|
if ([fetchSpecification prefetchingRelationshipKeyPaths]) //OK
|
|
qualArray = [NSMutableArray arrayWithCapacity: 5];
|
|
|
|
relationshipKeyPathEnum = [[fetchSpecification prefetchingRelationshipKeyPaths]
|
|
objectEnumerator];
|
|
enumNO=NULL;
|
|
while ((relationshipKeyPath = GDL2_NextObjectWithImpPtr(relationshipKeyPathEnum,&enumNO)))
|
|
{
|
|
IMP rkeyEnumNO=NULL; // nextObject
|
|
NSArray *relationshipKeyArray = [relationshipKeyPath
|
|
componentsSeparatedByString: @"."];
|
|
NSEnumerator *relationshipKeyEnum;
|
|
EORelationship *relationship;
|
|
EOEntity *currentEntity = entity;
|
|
NSString *relationshipKey;
|
|
|
|
relationshipKeyEnum = [relationshipKeyArray objectEnumerator];
|
|
while ((relationshipKey = GDL2_NextObjectWithImpPtr(relationshipKeyEnum,&rkeyEnumNO)))
|
|
{
|
|
relationship = [currentEntity relationshipNamed: relationshipKey];
|
|
currentEntity = [relationship destinationEntity];
|
|
|
|
// TODO rebuild object array for relationship path
|
|
|
|
[self _fetchRelationship: relationship
|
|
withObjects: array
|
|
editingContext: context];
|
|
}
|
|
}
|
|
|
|
if (_delegateRespondsTo.didFetchObjects == YES)
|
|
[_delegate databaseContext: self
|
|
didFetchObjects: array
|
|
fetchSpecification: fetchSpecification
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",@"step 1 channel is busy=%d",
|
|
(int)[channel isFetchInProgress]);
|
|
}
|
|
|
|
//
|
|
|
|
|
|
return array;
|
|
}
|
|
|
|
- (BOOL)isObjectLockedWithGlobalID: (EOGlobalID *)gid
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
return [self isObjectLockedWithGlobalID: gid];
|
|
}
|
|
|
|
- (void)lockObjectWithGlobalID: (EOGlobalID *)globalID
|
|
editingContext: (EOEditingContext *)context
|
|
{ // TODO
|
|
EOKeyGlobalID *gid = (EOKeyGlobalID *)globalID;
|
|
EODatabaseChannel *channel;
|
|
EOEntity *entity;
|
|
NSArray *attrsUsedForLocking, *primaryKeyAttributes;
|
|
NSDictionary *snapshot;
|
|
NSMutableDictionary *qualifierSnapshot, *lockSnapshot;
|
|
NSMutableArray *lockAttributes;
|
|
NSEnumerator *attrsEnum;
|
|
EOQualifier *qualifier = nil;
|
|
EOAttribute *attribute;
|
|
|
|
if ([self isObjectLockedWithGlobalID: gid] == NO)
|
|
{
|
|
IMP enumNO=NULL; // nextObject
|
|
snapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid);
|
|
|
|
if (_delegateRespondsTo.shouldLockObject == YES &&
|
|
[_delegate databaseContext: self
|
|
shouldLockObjectWithGlobalID: gid
|
|
snapshot: snapshot] == NO)
|
|
return;
|
|
|
|
/* If we do not have a snapshot yet, the the object
|
|
is probably faulted. The reference implementation seems
|
|
to ignore the lock in this case. We will try to do better
|
|
and if we can't, we'll acually raise as documented. */
|
|
if (snapshot == nil)
|
|
{
|
|
id obj = [context objectForGlobalID: gid];
|
|
if ([EOFault isFault: obj]) [obj self];
|
|
snapshot = [self snapshotForGlobalID: gid];
|
|
}
|
|
NSAssert1(snapshot,@"Could not obtain snapshot for %@", gid);
|
|
|
|
channel = [self availableChannel];
|
|
entity = [_database entityNamed: [gid entityName]];
|
|
|
|
NSAssert1(entity, @"No entity named %@",
|
|
[gid entityName]);
|
|
|
|
attrsUsedForLocking = [entity attributesUsedForLocking];
|
|
primaryKeyAttributes = [entity primaryKeyAttributes];
|
|
|
|
qualifierSnapshot = [NSMutableDictionary dictionaryWithCapacity: 16];
|
|
lockSnapshot = [NSMutableDictionary dictionaryWithCapacity: 8];
|
|
lockAttributes = [NSMutableArray arrayWithCapacity: 8];
|
|
|
|
attrsEnum = [primaryKeyAttributes objectEnumerator];
|
|
enumNO=NULL;
|
|
while ((attribute = GDL2_NextObjectWithImpPtr(attrsEnum,&enumNO)))
|
|
{
|
|
NSString *name = [attribute name];
|
|
|
|
[lockSnapshot setObject: [snapshot objectForKey:name]
|
|
forKey: name];
|
|
}
|
|
|
|
attrsEnum = [attrsUsedForLocking objectEnumerator];
|
|
enumNO=NULL;
|
|
while ((attribute = GDL2_NextObjectWithImpPtr(attrsEnum,&enumNO)))
|
|
{
|
|
NSString *name = [attribute name];
|
|
|
|
if ([primaryKeyAttributes containsObject:attribute] == NO)
|
|
{
|
|
if ([attribute adaptorValueType] == EOAdaptorBytesType)
|
|
{
|
|
[lockAttributes addObject: attribute];
|
|
[lockSnapshot setObject: [snapshot objectForKey:name]
|
|
forKey: name];
|
|
}
|
|
else
|
|
[qualifierSnapshot setObject: [snapshot objectForKey:name]
|
|
forKey: name];
|
|
}
|
|
}
|
|
|
|
// Turbocat
|
|
if ([[qualifierSnapshot allKeys] count] > 0)
|
|
qualifier = [EOAndQualifier
|
|
qualifierWithQualifiers:
|
|
[entity qualifierForPrimaryKey:
|
|
[entity primaryKeyForGlobalID: gid]],
|
|
[EOQualifier qualifierToMatchAllValues:
|
|
qualifierSnapshot],
|
|
nil];
|
|
|
|
if ([lockAttributes count] == 0)
|
|
lockAttributes = nil;
|
|
if ([lockSnapshot count] == 0)
|
|
lockSnapshot = nil;
|
|
|
|
if (_flags.beganTransaction == NO)
|
|
{
|
|
[[[channel adaptorChannel] adaptorContext] beginTransaction];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>YES");
|
|
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
|
|
NS_DURING
|
|
[[channel adaptorChannel] lockRowComparingAttributes: lockAttributes
|
|
entity: entity
|
|
qualifier: qualifier
|
|
snapshot: lockSnapshot];
|
|
NS_HANDLER
|
|
{
|
|
if (_delegateRespondsTo.shouldRaiseForLockFailure == YES)
|
|
{
|
|
if ([_delegate databaseContext: self
|
|
shouldRaiseExceptionForLockFailure:localException]
|
|
== YES)
|
|
[localException raise];
|
|
}
|
|
else
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
[self registerLockedObjectWithGlobalID: gid];
|
|
}
|
|
}
|
|
|
|
- (void)invalidateAllObjects
|
|
{
|
|
NSDictionary *snapshots;
|
|
NSArray *gids;
|
|
|
|
[_database invalidateResultCache];
|
|
|
|
snapshots = [_database snapshot];
|
|
gids = [snapshots allKeys];
|
|
[self invalidateObjectsWithGlobalIDs: gids];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: EOInvalidatedAllObjectsInStoreNotification
|
|
object: self];
|
|
}
|
|
|
|
- (void)invalidateObjectsWithGlobalIDs: (NSArray *)globalIDs
|
|
{
|
|
NSMutableArray *array = nil;
|
|
NSEnumerator *enumerator;
|
|
EOKeyGlobalID *gid;
|
|
|
|
if (_delegateRespondsTo.shouldInvalidateObject == YES)
|
|
{
|
|
IMP enumNO=NULL; // nextObject
|
|
array = [NSMutableArray array];
|
|
enumerator = [globalIDs objectEnumerator];
|
|
|
|
while ((gid = GDL2_NextObjectWithImpPtr(enumerator,&enumNO)))
|
|
{
|
|
if ([_delegate databaseContext: self
|
|
shouldInvalidateObjectWithGlobalID: gid
|
|
snapshot: EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid)] == YES)
|
|
[array addObject: gid];
|
|
}
|
|
}
|
|
|
|
[self forgetSnapshotsForGlobalIDs: ((id)array ? (id)array : globalIDs)];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EODatabaseContext(EOCooperatingObjectStoreSupport)
|
|
|
|
- (BOOL)ownsGlobalID: (EOGlobalID *)globalID
|
|
{
|
|
if ([globalID isKindOfClass: [EOKeyGlobalID class]] &&
|
|
[_database entityNamed: [globalID entityName]])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)ownsObject: (id)object
|
|
{
|
|
if ([_database entityForObject: object])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)ownsEntityNamed: (NSString *)entityName
|
|
{
|
|
if ([_database entityNamed: entityName])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)handlesFetchSpecification: (EOFetchSpecification *)fetchSpecification
|
|
{
|
|
//OK
|
|
if ([_database entityNamed: [fetchSpecification entityName]])
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
/* //Mirko:
|
|
- (EODatabaseOperation *)_dbOperationWithObject:object
|
|
operator:(EODatabaseOperator)operator
|
|
{
|
|
NSMapEnumerator gidEnum;
|
|
EODatabaseOperation *op;
|
|
EOGlobalID *gid;
|
|
|
|
gidEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
while (NSNextMapEnumeratorPair(&gidEnum, (void **)&gid, (void **)&op))
|
|
{
|
|
if ([[op object] isEqual:object] == YES)
|
|
{
|
|
if ([op databaseOperator] == operator)
|
|
return op;
|
|
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)_setGlobalID:(EOGlobalID *)globalID
|
|
forDatabaseOperation:(EODatabaseOperation *)op
|
|
{
|
|
EOGlobalID *oldGlobalID = [op globalID];
|
|
|
|
[op _setGlobalID:globalID];
|
|
|
|
NSMapInsert(_dbOperationsByGlobalID, globalID, op);
|
|
NSMapRemove(_dbOperationsByGlobalID, oldGlobalID);
|
|
}
|
|
|
|
- (EODatabaseOperation *)_dbOperationWithGlobalID:(EOGlobalID *)globalID
|
|
object:object
|
|
entity:(EOEntity *)entity
|
|
operator:(EODatabaseOperator)operator
|
|
{
|
|
EODatabaseOperation *op;
|
|
NSMutableDictionary *newRow;
|
|
NSMapEnumerator gidEnum;
|
|
EOAttribute *attribute;
|
|
EOGlobalID *gid;
|
|
NSString *key;
|
|
NSArray *classProperties;
|
|
BOOL found = NO;
|
|
int i, count;
|
|
id val;
|
|
|
|
gidEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
while (NSNextMapEnumeratorPair(&gidEnum, (void **)&gid, (void **)&op))
|
|
{
|
|
if ([[op object] isEqual:object] == YES)
|
|
{
|
|
found = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == YES)
|
|
return op;
|
|
|
|
if (globalID == nil)
|
|
globalID = AUTORELEASE([[EOTemporaryGlobalID alloc] init]);
|
|
|
|
op = AUTORELEASE([[EODatabaseOperation alloc] initWithGlobalID:globalID
|
|
object:object
|
|
entity:entity]);
|
|
|
|
[op setDatabaseOperator:operator];
|
|
[op setDBSnapshot:EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,globalID)];
|
|
|
|
newRow = [op newRow];
|
|
|
|
classProperties = [entity classProperties];
|
|
|
|
count = [classProperties count];
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[classProperties methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
attribute = GDL2_ObjectAtIndexWithImp(classProperties,oaiIMP,i);
|
|
if ([attribute isKindOfClass:GDL2_EOAttributeClass] == NO)
|
|
continue;
|
|
|
|
key = [attribute name];
|
|
|
|
if ([attribute isFlattened] == NO)
|
|
{
|
|
val = [object storedValueForKey:key];
|
|
|
|
if (val == nil)
|
|
val = GDL2_EONull;
|
|
|
|
[newRow setObject:val forKey:key];
|
|
}
|
|
}
|
|
};
|
|
|
|
NSMapInsert(_dbOperationsByGlobalID, globalID, op);
|
|
|
|
return op;
|
|
}
|
|
|
|
*/
|
|
|
|
// Prepares to save changes. Obtains primary keys for any inserted objects
|
|
// in the EditingContext that are owned by this context.
|
|
- (void)prepareForSaveWithCoordinator: (EOObjectStoreCoordinator *)coordinator
|
|
editingContext: (EOEditingContext *)context
|
|
{
|
|
//near OK
|
|
//Ayers: Review
|
|
NSArray *insertedObjects = nil;
|
|
NSMutableArray *noPKObjects = nil;
|
|
int round = 0;
|
|
|
|
|
|
NSAssert(context, @"No editing context");
|
|
|
|
_flags.preparingForSave = YES;
|
|
_coordinator=coordinator;//RETAIN ?
|
|
_editingContext=context;//RETAIN ?
|
|
|
|
// First, create dbOperation map if there's none
|
|
if (!_dbOperationsByGlobalID)
|
|
_dbOperationsByGlobalID = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
|
NSObjectMapValueCallBacks,
|
|
32);
|
|
|
|
// Next, build list of Entity which need PK generator
|
|
[self _buildPrimaryKeyGeneratorListForEditingContext: context];
|
|
|
|
// Now get newly inserted objects
|
|
// For each object, we will recordInsertForObject: and relay PK if it is !nil
|
|
insertedObjects = [context insertedObjects];
|
|
|
|
// We can make 2 rounds to try to get primary key for dependant objects
|
|
for(round=0;round<2;round++)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"round=%d [noPKObjects count]=%d",
|
|
round, [noPKObjects count]);
|
|
if (round==1 && [noPKObjects count]==0)
|
|
break;
|
|
else
|
|
{
|
|
NSArray* array=nil;
|
|
int i = 0;
|
|
int count = 0;
|
|
if (round==0)
|
|
array=insertedObjects;
|
|
else
|
|
{
|
|
array=noPKObjects;
|
|
NSDebugMLLog(@"EODatabaseContext",@"noPKObjects=%@",
|
|
noPKObjects);
|
|
}
|
|
count = [array count];
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[array methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id object = GDL2_ObjectAtIndexWithImp(array,oaiIMP,i);
|
|
|
|
|
|
|
|
if ([self ownsObject:object])
|
|
{
|
|
NSDictionary *objectPK = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
NSMutableDictionary *newRow = nil;
|
|
EOEntity *entity = [_database entityForObject:object];
|
|
|
|
if (round==0)
|
|
[self recordInsertForObject: object];
|
|
objectPK = [self _primaryKeyForObject: object
|
|
raiseException: round>0];
|
|
|
|
|
|
|
|
if (objectPK)
|
|
{
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p dbOpe=%@",
|
|
object,dbOpe);
|
|
|
|
newRow=[dbOpe newRow];
|
|
|
|
|
|
[self relayPrimaryKey: objectPK
|
|
object: object
|
|
entity: entity];
|
|
if (round>0)
|
|
{
|
|
[noPKObjects removeObjectAtIndex:i];
|
|
i--;
|
|
};
|
|
}
|
|
else if (round == 0)
|
|
{
|
|
if (!noPKObjects)
|
|
noPKObjects=(NSMutableArray*)[NSMutableArray array];
|
|
[noPKObjects addObject:object];
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- (void)recordChangesInEditingContext
|
|
{
|
|
IMP selfGIDFO=NULL; // _globalIDForObject:
|
|
int which = 0;
|
|
int c=0;
|
|
int i=0;
|
|
NSArray *objects[3] = {nil, nil, nil};
|
|
|
|
|
|
|
|
[self _assertValidStateWithSelector:
|
|
@selector(recordChangesInEditingContext)];
|
|
|
|
NSAssert(_editingContext, @"No editing context");
|
|
// We'll examin object in the following order:
|
|
// insertedObjects,
|
|
// deletedObjects (because re-inserted object should be removed from deleteds)
|
|
// updatedObjects (because inserted/deleted objects may cause some other objects to be updated).
|
|
|
|
NSMutableArray* recordToManySnapshot_dbOpes=[NSMutableArray array];
|
|
NSMutableArray* recordToManySnapshot_valuesGIDs=[NSMutableArray array];
|
|
NSMutableArray* recordToManySnapshot_relationshipNames=[NSMutableArray array];
|
|
|
|
NSMutableArray* nullifyAttributesInRelationship_relationships=[NSMutableArray array];
|
|
NSMutableArray* nullifyAttributesInRelationship_sourceObjects=[NSMutableArray array];
|
|
NSMutableArray* nullifyAttributesInRelationship_destinationObjects=[NSMutableArray array];
|
|
|
|
NSMutableArray* relayAttributesInRelationship_relationships=[NSMutableArray array];
|
|
NSMutableArray* relayAttributesInRelationship_sourceObjects=[NSMutableArray array];
|
|
NSMutableArray* relayAttributesInRelationship_destinationObjects=[NSMutableArray array];
|
|
|
|
for (which = 0; which < 3; which++)
|
|
{
|
|
int count = 0;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"Unprocessed: %@",
|
|
[_editingContext unprocessedDescription]);
|
|
NSDebugMLLog(@"EODatabaseContext", @"Objects: %@",
|
|
[_editingContext objectsDescription]);
|
|
|
|
|
|
if (which == 0)
|
|
objects[which] = [_editingContext insertedObjects];
|
|
else if (which == 1)
|
|
objects[which] = [_editingContext deletedObjects];
|
|
else
|
|
objects[which] = [_editingContext updatedObjects];
|
|
|
|
count = [objects[which] count];
|
|
|
|
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[objects[which] methodForSelector: @selector(objectAtIndex:)];
|
|
int i = 0;
|
|
|
|
// For each object
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSDictionary *currentCommittedSnapshot = nil;
|
|
NSArray *relationships = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
EOEntity *entity = nil;
|
|
id object = GDL2_ObjectAtIndexWithImp(objects[which],oaiIMP,i);
|
|
int relationshipsCount = 0;
|
|
IMP relObjectAtIndexIMP= NULL;
|
|
|
|
//Mirko ?? if ([self ownsObject:object] == YES)
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p (class=%@):\n%@",
|
|
object,
|
|
[object class],
|
|
object);
|
|
|
|
entity = [_database entityForObject: object]; //OK for Update
|
|
|
|
if (which == 0 || which == 2)//insert or update
|
|
{
|
|
NSDictionary *pk = nil;
|
|
NSDictionary *snapshot;
|
|
|
|
[self recordUpdateForObject: object //Why ForUpdate ? Becuase PK already generated ?
|
|
changes: nil]; //OK for update
|
|
|
|
// Get a dictionary of object properties+PK+relationships CURRENT values
|
|
snapshot = [object snapshot]; //OK for Update+Insert
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapshot %p: %@",
|
|
snapshot, snapshot);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"currentCommittedSnapshot %p: %@",
|
|
currentCommittedSnapshot,
|
|
currentCommittedSnapshot);
|
|
|
|
// Get a dictionary of object properties+PK+relationships DATABASES values
|
|
if (!currentCommittedSnapshot)
|
|
currentCommittedSnapshot =
|
|
[self _currentCommittedSnapshotForObject:object]; //OK For Update
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"currentCommittedSnapshot %p: %@",
|
|
currentCommittedSnapshot,
|
|
currentCommittedSnapshot);
|
|
|
|
//TODO so what ?
|
|
|
|
// Get the PK
|
|
pk = [self _primaryKeyForObject: object];//OK for Update
|
|
|
|
|
|
|
|
if (pk)
|
|
[self relayPrimaryKey: pk
|
|
object: object
|
|
entity: entity]; //OK for Update
|
|
}
|
|
|
|
relationships = [entity relationships]; //OK for Update
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",@"object=%p relationships: %@",
|
|
object,relationships);
|
|
|
|
relationshipsCount = [relationships count];
|
|
relObjectAtIndexIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
if (which == 1) //delete //Not in insert //not in update
|
|
{
|
|
if (relationshipsCount>0)
|
|
{
|
|
int iRelationship = 0;
|
|
|
|
for (iRelationship = 0; iRelationship < relationshipsCount;
|
|
iRelationship++)
|
|
{
|
|
EORelationship *relationship =
|
|
GDL2_ObjectAtIndexWithImp(relationships,relObjectAtIndexIMP,iRelationship);
|
|
|
|
if ([relationship isToManyToOne])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
[self recordDeleteForObject: object];
|
|
}
|
|
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
|
|
if (which == 0 || which == 2) //insert or update
|
|
{
|
|
//En update: dbsnapshot
|
|
//en insert : snapshot ? en insert:dbsnap aussi
|
|
int iRelationship = 0;
|
|
NSDictionary *snapshot = nil;
|
|
|
|
if (which == 0) //Insert //see wotRelSaveChanes.1.log seems to use dbSna for insert !
|
|
{
|
|
snapshot=[object snapshot];//NEW2
|
|
//snapshot=[dbOpe dbSnapshot]; //NEW
|
|
|
|
NSDebugMLog(@"[dbOpe dbSnapshot]=%@", [dbOpe dbSnapshot]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Insert: [dbOpe snapshot] %p=%@",
|
|
snapshot, snapshot);
|
|
}
|
|
else //Update
|
|
{
|
|
//NEWsnapshot=[dbOpe dbSnapshot];
|
|
snapshot = [object snapshot];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Update: [object snapshot] %p=%@",
|
|
snapshot, snapshot);
|
|
}
|
|
|
|
if (relationshipsCount>0)
|
|
{
|
|
for (iRelationship = 0; iRelationship < relationshipsCount;
|
|
iRelationship++)
|
|
{
|
|
NSArray *classProperties = nil;
|
|
EORelationship *substitutionRelationship = nil;
|
|
|
|
EORelationship *relationship =
|
|
GDL2_ObjectAtIndexWithImp(relationships,relObjectAtIndexIMP,iRelationship);
|
|
|
|
/*
|
|
get rel entity
|
|
entity model
|
|
model modelGroup
|
|
*/
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"HANDLE relationship %@ "
|
|
@"for object %p (class=%@):\n%@",
|
|
[relationship name],
|
|
object,
|
|
[object class],
|
|
object);
|
|
|
|
substitutionRelationship =
|
|
[relationship _substitutionRelationshipForRow: snapshot];
|
|
|
|
classProperties = [entity classProperties];
|
|
|
|
/*
|
|
rel name ==> toCountry
|
|
|
|
rel isToMany (0)
|
|
nullifyAttributesInRelationship:rel sourceObject:object destinationObject:nil (snapshot objectForKey: rel name ) ?
|
|
*/
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationship: %@", relationship);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"classProperties: %@",
|
|
classProperties);
|
|
|
|
if ([classProperties indexOfObjectIdenticalTo: relationship]
|
|
!= NSNotFound) //(or subst)
|
|
{
|
|
BOOL valuesAreEqual = NO;
|
|
BOOL isToMany = NO;
|
|
id relationshipCommitedSnapshotValue = nil;
|
|
NSString *relationshipName = [relationship name];
|
|
id relationshipSnapshotValue = nil;
|
|
|
|
|
|
|
|
//
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"snapshot for object %p:\n"
|
|
@"snapshot %p (count=%d)= \n%@\n\n",
|
|
object, snapshot, [snapshot count],
|
|
snapshot);
|
|
|
|
// substitutionRelationship objectForKey:
|
|
relationshipSnapshotValue =
|
|
[snapshot objectForKey: relationshipName];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipSnapshotValue "
|
|
@"(snapshot %p rel name=%@): %@",
|
|
snapshot,
|
|
relationshipName,
|
|
relationshipSnapshotValue);
|
|
|
|
if (which == 0) //Insert
|
|
currentCommittedSnapshot = [dbOpe dbSnapshot];
|
|
else //Update
|
|
{
|
|
if (!currentCommittedSnapshot)
|
|
currentCommittedSnapshot =
|
|
[self _currentCommittedSnapshotForObject: object]; //OK For Update
|
|
}
|
|
//update: _commited
|
|
//insert: dbSn
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"currentCommittedSnapshot %p: %@",
|
|
currentCommittedSnapshot,
|
|
currentCommittedSnapshot);
|
|
|
|
relationshipCommitedSnapshotValue =
|
|
[currentCommittedSnapshot objectForKey:
|
|
relationshipName];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipCommitedSnapshotValue "
|
|
@"(snapshot %p rel name=%@): %p",
|
|
currentCommittedSnapshot,
|
|
relationshipName,
|
|
relationshipCommitedSnapshotValue);
|
|
|
|
isToMany = [relationship isToMany];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"isToMany: %s",
|
|
(isToMany ? "YES" : "NO"));
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipSnapshotValue %p=%@",
|
|
relationshipSnapshotValue,
|
|
relationshipSnapshotValue);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipCommitedSnapshotValue %p=%@",
|
|
relationshipCommitedSnapshotValue,
|
|
(_isFault(relationshipCommitedSnapshotValue)
|
|
? (NSString*)@"[Fault]"
|
|
: (NSString*)relationshipCommitedSnapshotValue));
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"rel name=%@ relationshipCommitedSnapshotValue=%p relationshipSnapshotValue=%p",
|
|
relationshipName,
|
|
relationshipCommitedSnapshotValue,
|
|
relationshipSnapshotValue);
|
|
|
|
if (relationshipSnapshotValue
|
|
== relationshipCommitedSnapshotValue)
|
|
valuesAreEqual = YES;
|
|
else if (_isNilOrEONull(relationshipSnapshotValue))
|
|
valuesAreEqual = _isNilOrEONull(relationshipCommitedSnapshotValue);
|
|
else if (_isNilOrEONull(relationshipCommitedSnapshotValue))
|
|
valuesAreEqual = _isNilOrEONull(relationshipSnapshotValue);
|
|
else if (isToMany)
|
|
valuesAreEqual = [relationshipSnapshotValue
|
|
containsIdenticalObjectsWithArray:
|
|
relationshipCommitedSnapshotValue];
|
|
else // ToOne bu not same object
|
|
valuesAreEqual = NO;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p valuesAreEqual: %s",
|
|
object,(valuesAreEqual ? "YES" : "NO"));
|
|
|
|
if (valuesAreEqual)
|
|
{
|
|
//Equal Values !
|
|
}
|
|
else
|
|
{
|
|
if (isToMany)
|
|
{
|
|
NSDebugMLog(@"relationshipCommitedSnapshotValue=%@",relationshipCommitedSnapshotValue);
|
|
NSDebugMLog(@"relationshipSnapshotValue=%@",relationshipSnapshotValue);
|
|
//relationshipSnapshotValue shallowCopy
|
|
// Old Values are removed values
|
|
NSArray *oldValues = [relationshipCommitedSnapshotValue arrayExcludingObjectsInArray: relationshipSnapshotValue];
|
|
// Old Values are newly added values
|
|
NSArray *newValues = [relationshipSnapshotValue arrayExcludingObjectsInArray: relationshipCommitedSnapshotValue];
|
|
NSDebugMLog(@"oldValues=%@",oldValues);
|
|
NSDebugMLog(@"newValues=%@",newValues);
|
|
|
|
int oldValuesCount=[oldValues count];
|
|
int newValuesCount=[newValues count];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"oldValues count=%d",
|
|
[oldValues count]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"oldValues=%@",
|
|
oldValues);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"newValues count=%d",
|
|
[newValues count]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"newValues=%@",
|
|
newValues);
|
|
|
|
// Record ALL values snapshots
|
|
if (newValuesCount > 0)
|
|
{
|
|
int valuesCount = [relationshipSnapshotValue count];
|
|
int iValue = 0;
|
|
NSMutableArray *valuesGIDs = [NSMutableArray array];
|
|
IMP valuesGIDsAddObjectIMP=[valuesGIDs methodForSelector:@selector(addObject:)];
|
|
IMP svObjectAtIndexIMP=[relationshipSnapshotValue methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (iValue = 0;
|
|
iValue < valuesCount;
|
|
iValue++)
|
|
{
|
|
id aValue = GDL2_ObjectAtIndexWithImp(relationshipSnapshotValue,svObjectAtIndexIMP,iValue);
|
|
EOGlobalID *aValueGID = EODatabaseContext_globalIDForObjectWithImpPtr(self,&selfGIDFO,aValue);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"YYYY valuesGIDs=%@",
|
|
valuesGIDs);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"YYYY aValueGID=%@",
|
|
aValueGID);
|
|
GDL2_AddObjectWithImp(valuesGIDs,valuesGIDsAddObjectIMP,aValueGID);
|
|
}
|
|
|
|
NSDebugMLog(@"TEST20060216 relationshipName=%@ valuesGIDs=%@",relationshipName,valuesGIDs);
|
|
[recordToManySnapshot_dbOpes addObject:dbOpe];
|
|
[recordToManySnapshot_valuesGIDs addObject:valuesGIDs];
|
|
[recordToManySnapshot_relationshipNames addObject:relationshipName];
|
|
/*
|
|
[dbOpe recordToManySnapshot:valuesGIDs
|
|
relationshipName: relationshipName];
|
|
*/
|
|
}
|
|
|
|
// Nullify removed object relation attributes
|
|
if (oldValuesCount > 0)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"will call nullifyAttributes from source %p (class %@)",
|
|
object, [object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p=%@ (class=%@)",
|
|
object, object,
|
|
[object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipName=%@",
|
|
relationshipName);
|
|
[nullifyAttributesInRelationship_relationships addObject:relationship];
|
|
[nullifyAttributesInRelationship_sourceObjects addObject:object];
|
|
[nullifyAttributesInRelationship_destinationObjects addObject:oldValues];
|
|
/*
|
|
[self nullifyAttributesInRelationship:
|
|
relationship
|
|
sourceObject: object
|
|
destinationObjects: oldValues];
|
|
*/
|
|
}
|
|
|
|
// Relay relationship attributes in new objects
|
|
if (newValuesCount > 0)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"will call relay from source %p (class %@)",
|
|
object, [object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p=%@ (class=%@)",
|
|
object, object,
|
|
[object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipName=%@",
|
|
relationshipName);
|
|
|
|
[relayAttributesInRelationship_relationships addObject:relationship];
|
|
[relayAttributesInRelationship_sourceObjects addObject:object];
|
|
[relayAttributesInRelationship_destinationObjects addObject:newValues];
|
|
/*
|
|
[self relayAttributesInRelationship:
|
|
relationship
|
|
sourceObject: object
|
|
destinationObjects: newValues];
|
|
*/
|
|
}
|
|
}
|
|
else // To One
|
|
{
|
|
//id destinationObject=[object storedValueForKey:relationshipName];
|
|
|
|
if (!_isNilOrEONull(relationshipCommitedSnapshotValue)) // a value was removed
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"will call nullifyAttributes from source %p (class %@)",
|
|
object, [object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p=%@ (class=%@)",
|
|
object, object,
|
|
[object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipName=%@",
|
|
relationshipName);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destinationObject %p=%@ (class=%@)",
|
|
relationshipCommitedSnapshotValue,
|
|
relationshipCommitedSnapshotValue,
|
|
[relationshipCommitedSnapshotValue class]);
|
|
|
|
[nullifyAttributesInRelationship_relationships addObject:relationship];
|
|
[nullifyAttributesInRelationship_sourceObjects addObject:object];
|
|
[nullifyAttributesInRelationship_destinationObjects addObject:[NSArray arrayWithObject:relationshipCommitedSnapshotValue]];
|
|
/*
|
|
[self nullifyAttributesInRelationship:
|
|
relationship
|
|
sourceObject: object
|
|
destinationObject:
|
|
relationshipCommitedSnapshotValue];
|
|
*/
|
|
}
|
|
|
|
if (!_isNilOrEONull(relationshipSnapshotValue)) // a value was added
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"will call relay from source %p relname=%@",
|
|
object,
|
|
relationshipName);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p=%@ (class=%@)",
|
|
object, object,
|
|
[object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipName=%@",
|
|
relationshipName);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destinationObject %p=%@ (class=%@)",
|
|
relationshipSnapshotValue,
|
|
relationshipSnapshotValue,
|
|
[relationshipSnapshotValue class]);
|
|
|
|
[relayAttributesInRelationship_relationships addObject:relationship];
|
|
[relayAttributesInRelationship_sourceObjects addObject:object];
|
|
[relayAttributesInRelationship_destinationObjects addObject:[NSArray arrayWithObject:relationshipSnapshotValue]];
|
|
/* [self relayAttributesInRelationship:
|
|
relationship
|
|
sourceObject: object
|
|
destinationObject:
|
|
relationshipSnapshotValue];
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//!toMany:
|
|
//dbSnapshot was empty
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"will call nullifyAttributesInRelationship on source %p relname=%@",
|
|
object, [relationship name]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object %p=%@ (class=%@)",
|
|
object, object, [object class]);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipName=%@",
|
|
[relationship name]);
|
|
|
|
[nullifyAttributesInRelationship_relationships addObject:relationship];
|
|
[nullifyAttributesInRelationship_sourceObjects addObject:object];
|
|
[nullifyAttributesInRelationship_destinationObjects addObject:[NSArray array]];
|
|
/* [self nullifyAttributesInRelationship: relationship
|
|
sourceObject: object //CountryLabel
|
|
destinationObjects: nil];
|
|
*/
|
|
}
|
|
|
|
/*
|
|
NSMutableDictionary *row;
|
|
NSMutableArray *toManySnapshot, *newToManySnapshot;
|
|
NSArray *joins = [(EORelationship *)property joins];
|
|
NSString *joinName;
|
|
EOJoin *join;
|
|
int h, count;
|
|
id value;
|
|
|
|
name = [(EORelationship *)property name];
|
|
row = [NSMutableDictionary dictionaryWithCapacity:4];
|
|
count = [joins count];
|
|
|
|
|
|
if ([property isToMany] == YES)
|
|
{
|
|
NSMutableArray *toManyGIDArray;
|
|
NSArray *toManyObjects;
|
|
EOGlobalID *toManyGID;
|
|
id toManyObj;
|
|
|
|
|
|
toManySnapshot = AUTORRELEASE([[self snapshotForSourceGlobalID:gid
|
|
relationshipName:name]
|
|
mutableCopy]);
|
|
if (toManySnapshot == nil)
|
|
toManySnapshot = [NSMutableArray array];
|
|
|
|
|
|
newToManySnapshot = [NSMutableArray
|
|
arrayWithCapacity:10];
|
|
|
|
|
|
toManyObjects = [object storedValueForKey:name];
|
|
toManyGIDArray = [NSMutableArray
|
|
arrayWithCapacity:
|
|
[toManyObjects count]];
|
|
|
|
|
|
enumerator = [toManyObjects objectEnumerator];
|
|
while ((toManyObj = [enumerator nextObject]))
|
|
{
|
|
toManyGID = [_editingContext globalIDForObject:
|
|
toManyObj];
|
|
|
|
[toManyGIDArray addObject:toManyGID];
|
|
|
|
|
|
if ([toManySnapshot containsObject:toManyGID] == NO)
|
|
{
|
|
[newToManySnapshot addObject:toManyGID];
|
|
|
|
for (h=0; h<count; h++)
|
|
{
|
|
join = [joins objectAtIndex:h];
|
|
joinName = [[join sourceAttribute] name];
|
|
|
|
value = [snapshot objectForKey:joinName];
|
|
|
|
if (value)
|
|
[row setObject:value
|
|
forKey:joinName];
|
|
else
|
|
NSLog(@"warning: key name=%@ in entity=%@ not found in object %@", joinName, [entity name], object);
|
|
}
|
|
|
|
if ([self ownsObject:toManyObj] == YES)
|
|
{
|
|
EODatabaseOperation *dbOp;
|
|
|
|
dbOp = [self _dbOperationWithGlobalID:gid
|
|
object:object
|
|
entity:entity
|
|
operator:
|
|
EODatabaseUpdateOperator];
|
|
|
|
[[dbOp newRow] addEntriesFromDictionary:row];
|
|
}
|
|
else
|
|
{
|
|
[_coordinator
|
|
forwardUpdateForObject:toManyObj
|
|
changes:row];
|
|
}
|
|
}
|
|
else
|
|
[toManySnapshot removeObject:toManyGID];
|
|
}
|
|
|
|
[op recordToManySnapshot:toManyGIDArray
|
|
relationshipName:name];
|
|
|
|
#if 0 // TODO we have to clear foreign keys of a removed object ?
|
|
|
|
enumerator = [toManySnapshot objectEnumerator];
|
|
while ((toManyGID = [enumerator nextObject]))
|
|
{
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//IN relayAttributesInRelationship:sourceObject:destinationObject:
|
|
|
|
[[op newRow] addEntriesFromDictionary:row];
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
#if 0
|
|
countProp = [classProperties count];
|
|
for (i=0; i<countProp; i++)
|
|
{
|
|
NSString *name;
|
|
|
|
property = [classProperties objectAtIndex:i];
|
|
|
|
if ([property isKindOfClass:[EOAttribute class]])
|
|
{
|
|
name = [(EOAttribute *)property name];
|
|
|
|
if ([property isFlattened] == YES)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
////FIN
|
|
|
|
|
|
// TODO
|
|
/*
|
|
NSArray *changedObjects;
|
|
NSArray *classProperties;
|
|
NSEnumerator *objsEnum, *enumerator;
|
|
EOEntity *entity;
|
|
EODatabaseOperation *op;
|
|
EOGlobalID *gid;
|
|
int i, countProp;
|
|
id object, property;
|
|
|
|
NSDictionary *snapshot;
|
|
|
|
changedObjects = [_editingContext updatedObjects];
|
|
|
|
objsEnum = [changedObjects objectEnumerator];
|
|
while ((object = [objsEnum nextObject]))
|
|
{
|
|
if ([self ownsObject:object] == YES)
|
|
{
|
|
entity = [_database entityForObject:object];
|
|
gid = [_editingContext globalIDForObject:object];
|
|
|
|
|
|
//OK done
|
|
op = [self _dbOperationWithGlobalID:gid
|
|
object:object
|
|
entity:entity
|
|
operator:EODatabaseUpdateOperator];
|
|
///
|
|
|
|
|
|
snapshot = [op newRow];
|
|
|
|
classProperties = [entity classProperties];
|
|
|
|
countProp = [classProperties count];
|
|
for (i=0; i<countProp; i++)
|
|
{
|
|
NSString *name;
|
|
|
|
property = [classProperties objectAtIndex:i];
|
|
|
|
if ([property isKindOfClass:[EORelationship class]])
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
/*
|
|
//OK done
|
|
changedObjects = [_editingContext deletedObjects];
|
|
|
|
objsEnum = [changedObjects objectEnumerator];
|
|
while ((object = [objsEnum nextObject]))
|
|
{
|
|
if ([self ownsObject:object] == YES)
|
|
{
|
|
entity = [_database entityForObject:object];
|
|
gid = [_editingContext globalIDForObject:object];
|
|
// Turbocat
|
|
if (gid && ([gid isTemporary] == NO)) {
|
|
op = [self _dbOperationWithGlobalID:gid
|
|
object:object
|
|
entity:entity
|
|
operator:EODatabaseDeleteOperator];
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
c=[recordToManySnapshot_dbOpes count];
|
|
if (c>0)
|
|
{
|
|
IMP dbOpes_oaiIMP=
|
|
[recordToManySnapshot_dbOpes methodForSelector: @selector(objectAtIndex:)];
|
|
IMP valuesGIDs_oaiIMP=
|
|
[recordToManySnapshot_valuesGIDs methodForSelector: @selector(objectAtIndex:)];
|
|
IMP relationshipNames_oaiIMP=
|
|
[recordToManySnapshot_relationshipNames methodForSelector: @selector(objectAtIndex:)];
|
|
for(i=0;i<c;i++)
|
|
{
|
|
[GDL2_ObjectAtIndexWithImp(recordToManySnapshot_dbOpes,
|
|
dbOpes_oaiIMP,i)
|
|
recordToManySnapshot:
|
|
GDL2_ObjectAtIndexWithImp(recordToManySnapshot_valuesGIDs,
|
|
valuesGIDs_oaiIMP,i)
|
|
relationshipName:
|
|
GDL2_ObjectAtIndexWithImp(recordToManySnapshot_relationshipNames,
|
|
relationshipNames_oaiIMP,i)];
|
|
};
|
|
};
|
|
c=[nullifyAttributesInRelationship_relationships count];
|
|
if (c>0)
|
|
{
|
|
IMP relationships_oaiIMP=
|
|
[nullifyAttributesInRelationship_relationships methodForSelector: @selector(objectAtIndex:)];
|
|
IMP sourceObjects_oaiIMP=
|
|
[nullifyAttributesInRelationship_sourceObjects methodForSelector: @selector(objectAtIndex:)];
|
|
IMP destinationObjects_oaiIMP=
|
|
[nullifyAttributesInRelationship_destinationObjects methodForSelector: @selector(objectAtIndex:)];
|
|
for(i=0;i<c;i++)
|
|
{
|
|
[self nullifyAttributesInRelationship:
|
|
GDL2_ObjectAtIndexWithImp(nullifyAttributesInRelationship_relationships,
|
|
relationships_oaiIMP,i)
|
|
sourceObject:
|
|
GDL2_ObjectAtIndexWithImp(nullifyAttributesInRelationship_sourceObjects,
|
|
sourceObjects_oaiIMP,i)
|
|
destinationObjects:
|
|
GDL2_ObjectAtIndexWithImp(nullifyAttributesInRelationship_destinationObjects,
|
|
destinationObjects_oaiIMP,i)];
|
|
};
|
|
};
|
|
|
|
c=[relayAttributesInRelationship_relationships count];
|
|
if (c>0)
|
|
{
|
|
IMP relationships_oaiIMP=
|
|
[relayAttributesInRelationship_relationships methodForSelector: @selector(objectAtIndex:)];
|
|
IMP sourceObjects_oaiIMP=
|
|
[relayAttributesInRelationship_sourceObjects methodForSelector: @selector(objectAtIndex:)];
|
|
IMP destinationObjects_oaiIMP=
|
|
[relayAttributesInRelationship_destinationObjects methodForSelector: @selector(objectAtIndex:)];
|
|
for(i=0;i<c;i++)
|
|
{
|
|
[self relayAttributesInRelationship:
|
|
GDL2_ObjectAtIndexWithImp(relayAttributesInRelationship_relationships,
|
|
relationships_oaiIMP,i)
|
|
sourceObject:
|
|
GDL2_ObjectAtIndexWithImp(relayAttributesInRelationship_sourceObjects,
|
|
sourceObjects_oaiIMP,i)
|
|
destinationObjects:
|
|
GDL2_ObjectAtIndexWithImp(relayAttributesInRelationship_destinationObjects,
|
|
destinationObjects_oaiIMP,i)];
|
|
};
|
|
};
|
|
|
|
|
|
}
|
|
|
|
/** Contructs a list of EODatabaseOperations for all changes in the EOEditingContext
|
|
that are owned by this context. Forward any relationship changes discovered
|
|
but not owned by this context to the coordinator.
|
|
**/
|
|
- (void)recordUpdateForObject: (id)object
|
|
changes: (NSDictionary *)changes
|
|
{
|
|
//OK
|
|
EODatabaseOperation *dbOpe = nil;
|
|
|
|
|
|
NSAssert(object, @"No object");
|
|
|
|
|
|
|
|
|
|
[self _assertValidStateWithSelector:
|
|
@selector(recordUpdateForObject:changes:)];
|
|
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
[dbOpe setDatabaseOperator:EODatabaseUpdateOperator];
|
|
|
|
|
|
if ([changes count])
|
|
{
|
|
[[dbOpe newRow] addEntriesFromDictionary: changes];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
-(void)recordInsertForObject: (id)object
|
|
{
|
|
NSDictionary *snapshot = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
|
|
|
|
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
[dbOpe setDatabaseOperator: EODatabaseInsertOperator];
|
|
|
|
|
|
snapshot = [dbOpe dbSnapshot]; //TODO: sowhat
|
|
NSDebugMLLog(@"EODatabaseContext", @"object=%p snapshot=%@",
|
|
object, snapshot);
|
|
|
|
//call snapshot count
|
|
|
|
|
|
}
|
|
|
|
- (void) recordDeleteForObject: (id)object
|
|
{
|
|
NSDictionary *snapshot = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
|
|
|
|
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
[dbOpe setDatabaseOperator: EODatabaseDeleteOperator];
|
|
snapshot = [dbOpe dbSnapshot]; //TODO: sowhat
|
|
|
|
|
|
}
|
|
|
|
/** Constructs EOAdaptorOperations for all EODatabaseOperations constructed in
|
|
during recordChangesInEditingContext and recordUpdateForObject:changes:.
|
|
Performs the EOAdaptorOperations on an available adaptor channel.
|
|
If the save is OK, updates the snapshots in the EODatabaseContext to reflect
|
|
the new state of the server.
|
|
Raises an exception is the adaptor is unable to perform the operations.
|
|
**/
|
|
- (void)performChanges
|
|
{
|
|
NSMapEnumerator dbOpeEnum;
|
|
EOGlobalID *gid = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
NSArray *orderedAdaptorOperations = nil;
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
|
|
[self _assertValidStateWithSelector: @selector(performChanges)];
|
|
|
|
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
|
|
while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe))
|
|
{
|
|
|
|
|
|
//REVOIR
|
|
if ([dbOpe databaseOperator] == EODatabaseNothingOperator)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!",
|
|
dbOpe);
|
|
}
|
|
else
|
|
{
|
|
[self _verifyNoChangesToReadonlyEntity: dbOpe];
|
|
//MIRKO snapshot = [op dbSnapshot];
|
|
[self createAdaptorOperationsForDatabaseOperation: dbOpe];
|
|
}
|
|
}
|
|
// avoid leaks! -- dw
|
|
NSEndMapTableEnumeration(&dbOpeEnum);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"orderedAdaptorOperations A=%@",
|
|
orderedAdaptorOperations);
|
|
|
|
orderedAdaptorOperations = [self orderAdaptorOperations];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"orderedAdaptorOperations B=%@",
|
|
orderedAdaptorOperations);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
|
|
if ([orderedAdaptorOperations count] > 0)
|
|
{
|
|
EOAdaptorChannel *adaptorChannel = nil;
|
|
EODatabaseChannel *dbChannel = [self _obtainOpenChannel];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
|
|
if (_flags.beganTransaction == NO)//MIRKO
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p [_adaptorContext transactionNestingLevel]=%d",
|
|
self,
|
|
(int)[_adaptorContext transactionNestingLevel]);
|
|
|
|
if ([_adaptorContext transactionNestingLevel] == 0) //??
|
|
[_adaptorContext beginTransaction];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>YES");
|
|
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
|
|
adaptorChannel = [dbChannel adaptorChannel];
|
|
|
|
if (_delegateRespondsTo.willPerformAdaptorOperations == YES)
|
|
orderedAdaptorOperations = [_delegate databaseContext: self
|
|
willPerformAdaptorOperations:
|
|
orderedAdaptorOperations
|
|
adaptorChannel: adaptorChannel];
|
|
NS_DURING
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"performAdaptorOperations:");
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
|
|
[adaptorChannel performAdaptorOperations: orderedAdaptorOperations];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"after performAdaptorOperations:");
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Exception in performAdaptorOperations:%@",
|
|
localException);
|
|
[localException raise];
|
|
//MIRKO
|
|
//TODO
|
|
/*
|
|
NSException *exp;
|
|
NSMutableDictionary *userInfo;
|
|
EOAdaptorOperation *adaptorOp;
|
|
|
|
userInfo = [NSMutableDictionary dictionaryWithCapacity:10];
|
|
[userInfo addEntriesFromDictionary:[localException userInfo]];
|
|
[userInfo setObject:self forKey:EODatabaseContextKey];
|
|
[userInfo setObject:dbOps
|
|
forKey:EODatabaseOperationsKey];
|
|
|
|
adaptorOp = [userInfo objectForKey:EOFailedAdaptorOperationKey];
|
|
|
|
dbEnum = [dbOps objectEnumerator];
|
|
while ((op = [dbEnum nextObject]))
|
|
if ([[op adaptorOperations] containsObject:adaptorOp] == YES)
|
|
{
|
|
[userInfo setObject:op
|
|
forKey:EOFailedDatabaseOperationKey];
|
|
break;
|
|
}
|
|
|
|
exp = [NSException exceptionWithName:EOGeneralDatabaseException
|
|
reason:[NSString stringWithFormat:
|
|
@"%@ -- %@ 0x%x: failed with exception name:%@ reason:\"%@\"",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self, [localException name],
|
|
[localException reason]]
|
|
userInfo:userInfo];
|
|
|
|
[exp raise];
|
|
*/
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
//This is not done by mirko:
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p preparingForSave=%d beganTransaction=%d",
|
|
self,
|
|
(int)_flags.preparingForSave,
|
|
(int)_flags.beganTransaction);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p _uniqueStack %p=%@",
|
|
self, _uniqueStack, _uniqueStack);
|
|
|
|
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
|
|
while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid,
|
|
(void **)&dbOpe))
|
|
{
|
|
EODatabaseOperator databaseOperator = EODatabaseNothingOperator;
|
|
|
|
//call dbOpe adaptorOperations ?
|
|
if ([dbOpe databaseOperator] == EODatabaseNothingOperator)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Db Ope %@ for Nothing !!!", dbOpe);
|
|
}
|
|
else
|
|
{
|
|
EOEntity *entity = nil;
|
|
NSArray *dbSnapshotKeys = nil;
|
|
NSMutableDictionary *newRow = nil;
|
|
NSDictionary *values = nil;
|
|
id object = nil;
|
|
NSArray *adaptorOpe = nil;
|
|
|
|
|
|
|
|
object = [dbOpe object];
|
|
adaptorOpe = [dbOpe adaptorOperations];
|
|
databaseOperator = [dbOpe databaseOperator];
|
|
entity = [dbOpe entity];
|
|
dbSnapshotKeys = [entity dbSnapshotKeys];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"dbSnapshotKeys=%@",
|
|
dbSnapshotKeys);
|
|
|
|
newRow = [dbOpe newRow];
|
|
|
|
|
|
values = [newRow valuesForKeys: dbSnapshotKeys];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"RECORDSNAPSHOT values=%@", values);
|
|
//if update: forgetSnapshotForGlobalID:
|
|
|
|
[self recordSnapshot: values
|
|
forGlobalID: gid];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p _uniqueStack %p=%@",
|
|
self, _uniqueStack, _uniqueStack);
|
|
|
|
if (databaseOperator == EODatabaseUpdateOperator) //OK for update //Do it forInsert too //TODO
|
|
{
|
|
NSDictionary *toManySnapshots = [dbOpe toManySnapshots];
|
|
|
|
if (toManySnapshots)
|
|
{
|
|
NSDebugMLog(@"toManySnapshots=%@", toManySnapshots);
|
|
NSEmitTODO();
|
|
|
|
//TODONOW [self notImplemented: _cmd]; //TODO
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// avoid leaks! -- dw
|
|
NSEndMapTableEnumeration(&dbOpeEnum);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// TODO: change to SYNCRONIZED and free memorory properly -- dw
|
|
- (void)commitChanges
|
|
{
|
|
BOOL doIt = NO;
|
|
NSMutableArray *deletedObjects = [NSMutableArray array];
|
|
NSMutableArray *insertedObjects = [NSMutableArray array];
|
|
NSMutableArray *updatedObjects = [NSMutableArray array];
|
|
NSMutableDictionary *gidChangedUserInfo = nil;
|
|
|
|
|
|
[self _assertValidStateWithSelector: @selector(commitChanges)];
|
|
|
|
//REVOIR: don't do it if no adaptor ope
|
|
{
|
|
NSMapEnumerator dbOpeEnum;
|
|
EOGlobalID *gid = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
|
|
while (!doIt && NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid,
|
|
(void **)&dbOpe))
|
|
{
|
|
doIt = ([dbOpe adaptorOperations] != nil);
|
|
}
|
|
// avoid leaks! -- dw
|
|
NSEndMapTableEnumeration(&dbOpeEnum);
|
|
}
|
|
|
|
/* If a transaction is open, it could be holding locks
|
|
which we want to release. */
|
|
if (doIt || _flags.beganTransaction)
|
|
{
|
|
if (_flags.beganTransaction == NO)//it is not set here in WO
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else if ([_adaptorContext transactionNestingLevel] == 0)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSMapEnumerator dbOpeEnum;
|
|
EOGlobalID *gid = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>NO");
|
|
|
|
_flags.beganTransaction = NO;
|
|
[_adaptorContext commitTransaction]; //adaptorcontext transactionDidCommit
|
|
|
|
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid,
|
|
(void **)&dbOpe))
|
|
{
|
|
EODatabaseOperator databaseOperator = EODatabaseNothingOperator;
|
|
EOGlobalID *dbOpeGID = nil;
|
|
EOGlobalID *newGID = nil;
|
|
EOEntity *entity = nil;
|
|
|
|
|
|
|
|
[EOObserverCenter suppressObserverNotification];
|
|
|
|
NS_DURING
|
|
{
|
|
databaseOperator = [dbOpe databaseOperator];
|
|
entity = [dbOpe entity];
|
|
|
|
if (databaseOperator == EODatabaseInsertOperator
|
|
|| databaseOperator == EODatabaseUpdateOperator)//OK for update
|
|
{
|
|
id object = nil;
|
|
NSDictionary *newRowValues = nil;
|
|
//gid isTemporary
|
|
NSDictionary *primaryKeyDiffs = [dbOpe primaryKeyDiffs];//OK for update
|
|
|
|
if (primaryKeyDiffs)
|
|
{
|
|
NSEmitTODO();
|
|
NSAssert3(NO,@"primaryKeyDiffs=%@ dbOpe=%@ object=%@",
|
|
primaryKeyDiffs,dbOpe,[dbOpe object]); //TODO: if primaryKeyDiffs
|
|
}
|
|
|
|
if (databaseOperator == EODatabaseInsertOperator)
|
|
{
|
|
NSArray *classPropertyAttributeNames =
|
|
[entity classPropertyAttributeNames];
|
|
NSDictionary *newRow = [dbOpe newRow];
|
|
|
|
newRowValues = [newRow valuesForKeys:
|
|
classPropertyAttributeNames];
|
|
|
|
//TODO REVOIR !!
|
|
newGID = [entity globalIDForRow: newRow
|
|
isFinal: YES];
|
|
}
|
|
else
|
|
{
|
|
NSArray *classPropertyAttributes = [entity _classPropertyAttributes];//OK for update
|
|
|
|
newRowValues = [dbOpe rowDiffsForAttributes: classPropertyAttributes];//OK for update
|
|
}
|
|
|
|
object = [dbOpe object];//OK for update
|
|
[object takeStoredValuesFromDictionary: newRowValues];//OK for update
|
|
|
|
/*mirko instead:
|
|
[object takeStoredValuesFromDictionary:
|
|
[op rowDiffsForAttributes:attributes]];
|
|
|
|
|
|
|
|
[_database recordSnapshot:[op newRow]
|
|
forGlobalID:gid];
|
|
|
|
toManySnapshots = [op toManySnapshots];
|
|
toManyEnum = [toManySnapshots keyEnumerator];
|
|
while ((key = [toManyEnum nextObject]))
|
|
[_database recordSnapshot:[toManySnapshots objectForKey:key]
|
|
forSourceGlobalID:gid
|
|
relationshipName:key];
|
|
*/
|
|
}
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
[EOObserverCenter enableObserverNotification];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"EXCEPTION %@",
|
|
localException);
|
|
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
[EOObserverCenter enableObserverNotification];
|
|
|
|
|
|
dbOpeGID = [dbOpe globalID]; //OK for update
|
|
|
|
|
|
switch (databaseOperator)
|
|
{
|
|
case EODatabaseInsertOperator:
|
|
[insertedObjects addObject: dbOpeGID];
|
|
|
|
if (!gidChangedUserInfo)
|
|
gidChangedUserInfo = (NSMutableDictionary *)
|
|
[NSMutableDictionary dictionary];
|
|
|
|
[gidChangedUserInfo setObject: newGID
|
|
forKey: dbOpeGID]; //[_editingContext globalIDForObject:object]];
|
|
break;
|
|
|
|
case EODatabaseDeleteOperator:
|
|
[deletedObjects addObject: dbOpeGID];
|
|
[_database forgetSnapshotForGlobalID: dbOpeGID]; //Mirko/??
|
|
break;
|
|
|
|
case EODatabaseUpdateOperator:
|
|
[updatedObjects addObject: dbOpeGID];
|
|
break;
|
|
|
|
case EODatabaseNothingOperator:
|
|
break;
|
|
}
|
|
}
|
|
// avoid leaks! -- dw
|
|
NSEndMapTableEnumeration(&dbOpeEnum);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[self _cleanUpAfterSave];//OK for update
|
|
|
|
if (doIt)
|
|
{
|
|
//from mirko. seems ok
|
|
if (gidChangedUserInfo)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"post EOGlobalIDChangedNotification");
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: EOGlobalIDChangedNotification
|
|
object: nil
|
|
userInfo: gidChangedUserInfo];
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"post EOObjectsChangedInStoreNotification");
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName: @"EOObjectsChangedInStoreNotification"
|
|
object: _database
|
|
userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
|
|
deletedObjects, EODeletedKey,
|
|
insertedObjects, EOInsertedKey,
|
|
updatedObjects, EOUpdatedKey,
|
|
nil, nil]]; //call self _snapshotsChangedInDatabase:
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)rollbackChanges
|
|
{ // TODO
|
|
//adaptorcontext transactionNestingLevel
|
|
//if 0 ? _cleanUpAfterSave
|
|
|
|
|
|
if (_flags.beganTransaction == YES)
|
|
{
|
|
[_adaptorContext rollbackTransaction];
|
|
|
|
|
|
|
|
_flags.beganTransaction = NO;
|
|
|
|
if (_lockedObjects)
|
|
{
|
|
NSResetHashTable(_lockedObjects);
|
|
}
|
|
|
|
NSResetMapTable(_dbOperationsByGlobalID);
|
|
/* //TODO
|
|
[_snapshots removeAllObjects];
|
|
[_toManySnapshots removeAllObjects];
|
|
*/
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (NSDictionary *)valuesForKeys: (NSArray *)keys
|
|
object: (id)object
|
|
{
|
|
//OK
|
|
EOEntity *entity;
|
|
EODatabaseOperation *dbOpe;
|
|
NSDictionary *newRow;
|
|
NSDictionary *values = nil;
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"object=%p (class=%@)",
|
|
object, [object class]);
|
|
|
|
//NSAssert(object, @"No object");
|
|
|
|
if (!_isNilOrEONull(object))
|
|
{
|
|
entity = [_database entityForObject: object];
|
|
|
|
NSAssert1(entity, @"No entity for object %@", object);
|
|
|
|
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
|
|
|
|
newRow = [dbOpe newRow];
|
|
|
|
|
|
|
|
|
|
values = [newRow valuesForKeys: keys];
|
|
}
|
|
else
|
|
{
|
|
|
|
values = [NSDictionary dictionary];
|
|
}
|
|
|
|
//
|
|
|
|
|
|
|
|
return values;
|
|
}
|
|
|
|
-(void)nullifyAttributesInRelationship: (EORelationship*)relationship
|
|
sourceObject: (id)sourceObject
|
|
destinationObject: (id)destinationObject
|
|
{
|
|
EODatabaseOperation *sourceDBOpe = nil;
|
|
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationObject=%@",
|
|
destinationObject);
|
|
|
|
if (destinationObject)
|
|
{
|
|
//Get SourceObject database operation
|
|
sourceDBOpe = [self databaseOperationForObject: sourceObject]; //TODO: useIt
|
|
|
|
|
|
|
|
if ([relationship isToManyToOne])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
// Key a dictionary of two array: destinationKeys and sourceKeys
|
|
NSDictionary *sourceToDestinationKeyMap =
|
|
[relationship _sourceToDestinationKeyMap]; //{destinationKeys = (customerCode); sourceKeys = (code); }
|
|
BOOL foreignKeyInDestination = [relationship foreignKeyInDestination];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceToDestinationKeyMap=%@",
|
|
sourceToDestinationKeyMap);
|
|
NSDebugMLLog(@"EODatabaseContext", @"foreignKeyInDestination=%d",
|
|
foreignKeyInDestination);
|
|
|
|
if (foreignKeyInDestination)
|
|
{
|
|
NSArray *destinationKeys = [sourceToDestinationKeyMap
|
|
objectForKey: @"destinationKeys"];//(customerCode)
|
|
int i, destinationKeysCount = [destinationKeys count];
|
|
NSMutableDictionary *changes = [NSMutableDictionary dictionaryWithCapacity: destinationKeysCount];
|
|
|
|
if (destinationKeysCount>0)
|
|
{
|
|
IMP oaiIMP=[destinationKeys methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0 ;i < destinationKeysCount; i++)
|
|
{
|
|
id destinationKey = GDL2_ObjectAtIndexWithImp(destinationKeys,oaiIMP,i);
|
|
|
|
[changes setObject: GDL2_EONull
|
|
forKey: destinationKey];
|
|
}
|
|
}
|
|
|
|
NSAssert1(destinationObject,
|
|
@"No destinationObject for call of recordUpdateForObject:changes: changes: %@",
|
|
changes);
|
|
|
|
[self recordUpdateForObject: destinationObject
|
|
changes: changes];
|
|
}
|
|
else
|
|
{
|
|
//Do nothing ?
|
|
NSEmitTODO();
|
|
//[self notImplemented: _cmd]; //TODO
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)nullifyAttributesInRelationship: (EORelationship*)relationship
|
|
sourceObject: (id)sourceObject
|
|
destinationObjects: (NSArray*)destinationObjects
|
|
{
|
|
int destinationObjectsCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationObjects=%@",
|
|
destinationObjects);
|
|
|
|
destinationObjectsCount = [destinationObjects count];
|
|
|
|
if (destinationObjectsCount > 0)
|
|
{
|
|
int i;
|
|
IMP oaiIMP=[destinationObjects methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < destinationObjectsCount; i++)
|
|
{
|
|
id object = GDL2_ObjectAtIndexWithImp(destinationObjects,oaiIMP,i);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destinationObject %p=%@ (class %@)",
|
|
object, object, [object class]);
|
|
|
|
[self nullifyAttributesInRelationship: relationship
|
|
sourceObject: sourceObject
|
|
destinationObject: object];
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)relayAttributesInRelationship: (EORelationship*)relationship
|
|
sourceObject: (id)sourceObject
|
|
destinationObjects: (NSArray*)destinationObjects
|
|
{
|
|
int destinationObjectsCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationObjects=%@",
|
|
destinationObjects);
|
|
|
|
destinationObjectsCount = [destinationObjects count];
|
|
|
|
if (destinationObjectsCount > 0)
|
|
{
|
|
int i;
|
|
IMP oaiIMP=[destinationObjects methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < destinationObjectsCount; i++)
|
|
{
|
|
id object = GDL2_ObjectAtIndexWithImp(destinationObjects,oaiIMP,i);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destinationObject %p=%@ (class %@)",
|
|
object, object, [object class]);
|
|
|
|
[self relayAttributesInRelationship: (EORelationship*)relationship
|
|
sourceObject: (id)sourceObject
|
|
destinationObject: object];
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (NSDictionary*)relayAttributesInRelationship: (EORelationship*)relationship
|
|
sourceObject: (id)sourceObject
|
|
destinationObject: (id)destinationObject
|
|
{
|
|
//OK
|
|
NSMutableDictionary *relayedValues = nil;
|
|
EODatabaseOperation *sourceDBOpe = nil;
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceObject %p=%@ (class=%@)",
|
|
sourceObject, sourceObject, [sourceObject class]);
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationObject %p=%@ (class=%@)",
|
|
destinationObject, destinationObject,
|
|
[destinationObject class]);
|
|
|
|
//Get SourceObject database operation
|
|
sourceDBOpe = [self databaseOperationForObject: sourceObject];
|
|
|
|
|
|
|
|
if ([sourceDBOpe databaseOperator] == EODatabaseNothingOperator)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!",
|
|
sourceDBOpe);
|
|
}
|
|
|
|
if ([relationship isToManyToOne])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
// Key a dictionary of two array: destinationKeys and sourceKeys
|
|
NSDictionary *sourceToDestinationKeyMap = [relationship _sourceToDestinationKeyMap];//{destinationKeys = (customerCode); sourceKeys = (code); }
|
|
NSArray *destinationKeys = [sourceToDestinationKeyMap
|
|
objectForKey: @"destinationKeys"];//(customerCode)
|
|
NSArray *sourceKeys = [sourceToDestinationKeyMap
|
|
objectForKey: @"sourceKeys"];//(code)
|
|
NSMutableDictionary *sourceNewRow = [sourceDBOpe newRow];//OK in foreignKeyInDestination
|
|
BOOL foreignKeyInDestination = [relationship foreignKeyInDestination];
|
|
int i, count;
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceToDestinationKeyMap=%@",
|
|
sourceToDestinationKeyMap);
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationKeys=%@",
|
|
destinationKeys);
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"foreignKeyInDestination=%s",
|
|
(foreignKeyInDestination ? "YES" : "NO"));
|
|
|
|
NSAssert([destinationKeys count] == [sourceKeys count],
|
|
@"destination keys count!=source keys count");
|
|
|
|
if (foreignKeyInDestination || [relationship propagatesPrimaryKey])
|
|
{
|
|
IMP srcObjectAIndexIMP=[sourceKeys methodForSelector: @selector(objectAtIndex:)];
|
|
IMP dstObjectAIndexIMP=[destinationKeys methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
relayedValues = AUTORELEASE([[sourceNewRow valuesForKeys: sourceKeys]
|
|
mutableCopy]);// {code = 0; }
|
|
NSDebugMLLog(@"EODatabaseContext", @"relayedValues=%@",
|
|
relayedValues);
|
|
|
|
count = [relayedValues count];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSString *sourceKey = GDL2_ObjectAtIndexWithImp(sourceKeys,srcObjectAIndexIMP,i);
|
|
NSString *destKey = GDL2_ObjectAtIndexWithImp(destinationKeys,dstObjectAIndexIMP,i);
|
|
id sourceValue = [relayedValues objectForKey: sourceKey];
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"sourceValue=%@",
|
|
sourceValue);
|
|
|
|
[relayedValues removeObjectForKey: sourceKey];
|
|
[relayedValues setObject: sourceValue
|
|
forKey: destKey];
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relayedValues=%@",
|
|
relayedValues);
|
|
|
|
NSAssert1(destinationObject,
|
|
@"No destinationObject for call of "
|
|
@"recordUpdateForObject:changes: relayedValues: %@",
|
|
relayedValues);
|
|
|
|
[self recordUpdateForObject: destinationObject
|
|
changes: relayedValues];
|
|
}
|
|
else
|
|
{
|
|
//Verify !!
|
|
NSDictionary *destinationValues;
|
|
IMP srcObjectAIndexIMP=[sourceKeys methodForSelector: @selector(objectAtIndex:)];
|
|
IMP dstObjectAIndexIMP=[destinationKeys methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Call valuesForKeys destinationObject (%p-<%@>)",
|
|
destinationObject, [destinationObject class]);
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationKeys=%@",
|
|
destinationKeys);
|
|
|
|
|
|
|
|
//Now take destinationKeys values
|
|
destinationValues = [self valuesForKeys: destinationKeys
|
|
object: destinationObject];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationValues=%@",
|
|
destinationValues);
|
|
//And put these values for source keys in the return object (sourceValues)
|
|
|
|
count = [destinationKeys count];
|
|
relayedValues = (NSMutableDictionary*)[NSMutableDictionary dictionary];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relayedValues=%@",
|
|
relayedValues);
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSString *sourceKey = GDL2_ObjectAtIndexWithImp(sourceKeys,srcObjectAIndexIMP,i);
|
|
NSString *destinationKey = GDL2_ObjectAtIndexWithImp(destinationKeys,dstObjectAIndexIMP,i);
|
|
id destinationValue = [destinationValues
|
|
objectForKey: destinationKey];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"destinationKey=%@",
|
|
destinationKey);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destinationValue=%@", destinationValue);
|
|
|
|
if (!_isNilOrEONull(destinationValue))//?? or always
|
|
[relayedValues setObject: destinationValue
|
|
forKey: sourceKey];
|
|
}
|
|
//Put these values in source object database ope new row
|
|
NSDebugMLLog(@"EODatabaseContext", @"relayedValues=%@",
|
|
relayedValues);
|
|
|
|
[sourceNewRow takeValuesFromDictionary: relayedValues];
|
|
}
|
|
}
|
|
|
|
if ([sourceDBOpe databaseOperator] == EODatabaseNothingOperator)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Db Ope %@ for Nothing !!!", sourceDBOpe);
|
|
}
|
|
|
|
|
|
|
|
return relayedValues;
|
|
//Mirko Code:
|
|
/*
|
|
NSMutableArray *keys;
|
|
NSDictionary *values;
|
|
EOGlobalID *relationshipGID;
|
|
id objectTo;
|
|
|
|
objectTo = [object storedValueForKey:name];
|
|
relationshipGID = [_editingContext
|
|
globalIDForObject:objectTo];
|
|
if ([self ownsObject:objectTo] == YES)
|
|
{
|
|
for (h=0; h<count; h++)
|
|
{
|
|
join = [joins objectAtIndex:h];
|
|
|
|
value = [objectTo
|
|
storedValueForKey:
|
|
[[join destinationAttribute] name]];
|
|
|
|
if (value == nil)
|
|
value = GDL2_EONull;
|
|
|
|
[row setObject:value
|
|
forKey:[[join sourceAttribute] name]];
|
|
}
|
|
#if 0
|
|
inverse = [property inverseRelationship];
|
|
if (inverse)
|
|
{
|
|
toManySnapshot = AUTORELEASE([[self snapshotForSourceGlobalID:gid
|
|
relationshipName:[inverse name]]
|
|
mutableCopy]);
|
|
if (toManySnapshot == nil)
|
|
toManySnapshot = [NSMutableArray array];
|
|
|
|
[toManySnapshot addObject:gid];
|
|
|
|
[op recordToManySnapshot:toManySnapshot
|
|
relationshipName:name];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
keys = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for (h=0; h<count; h++)
|
|
{
|
|
join = [joins objectAtIndex:h];
|
|
[keys addObject:[[join destinationAttribute]
|
|
name]];
|
|
}
|
|
|
|
values = [_coordinator valuesForKeys:keys
|
|
object:objectTo];
|
|
|
|
for (h=0; h<count; h++)
|
|
{
|
|
join = [joins objectAtIndex:h];
|
|
|
|
value = [values
|
|
objectForKey:
|
|
[[join destinationAttribute] name]];
|
|
|
|
if (value == nil)
|
|
value = GDL2_EONull;
|
|
|
|
[row setObject:value
|
|
forKey:[[join sourceAttribute] name]];
|
|
}
|
|
}
|
|
*/
|
|
|
|
};
|
|
|
|
- (void)recordDatabaseOperation: (EODatabaseOperation*)databaseOpe
|
|
{
|
|
//OK
|
|
EOGlobalID *gid = nil;
|
|
|
|
|
|
|
|
NSAssert(databaseOpe, @"No database operation");
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"_dbOperationsByGlobalID=%p",
|
|
_dbOperationsByGlobalID);
|
|
|
|
if (_dbOperationsByGlobalID)
|
|
{
|
|
//
|
|
NSDebugMLLog(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
|
|
NSStringFromMapTable(_dbOperationsByGlobalID));
|
|
|
|
/*
|
|
// doesn't do this so some db operation are not recorded (when selecting objects)
|
|
if (!_dbOperationsByGlobalID)
|
|
_dbOperationsByGlobalID = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
|
NSObjectMapValueCallBacks,
|
|
32);
|
|
*/
|
|
gid = [databaseOpe globalID];
|
|
|
|
|
|
|
|
NSMapInsert(_dbOperationsByGlobalID, gid, databaseOpe);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"_dbOperationsByGlobalID=%p",
|
|
_dbOperationsByGlobalID);
|
|
NSDebugMLLog(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
|
|
NSStringFromMapTable(_dbOperationsByGlobalID));
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"No _dbOperationsByGlobalID");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (EODatabaseOperation*)databaseOperationForGlobalID: (EOGlobalID*)gid
|
|
{
|
|
//OK
|
|
EODatabaseOperation *dbOpe = nil;
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"_dbOperationsByGlobalID=%p",
|
|
_dbOperationsByGlobalID);
|
|
|
|
if (_dbOperationsByGlobalID)
|
|
{
|
|
//
|
|
NSDebugMLLog(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
|
|
NSStringFromMapTable(_dbOperationsByGlobalID));
|
|
|
|
dbOpe = (EODatabaseOperation*)NSMapGet(_dbOperationsByGlobalID,
|
|
(const void*)gid);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return dbOpe;
|
|
}
|
|
|
|
- (EODatabaseOperation*)databaseOperationForObject: (id)object
|
|
{
|
|
//OK
|
|
EODatabaseOperation *databaseOpe = nil;
|
|
EOGlobalID *gid = nil;
|
|
|
|
|
|
|
|
NS_DURING // for trace purpose
|
|
{
|
|
|
|
|
|
if ([object isKindOfClass: [EOGenericRecord class]])
|
|
NSDebugMLLog(@"EODatabaseContext", @"dictionary=%@ ",
|
|
[object debugDictionaryDescription]);
|
|
|
|
gid = EODatabaseContext_globalIDForObjectWithImpPtr(self,NULL,object);
|
|
|
|
|
|
databaseOpe = [self databaseOperationForGlobalID: gid]; //OK
|
|
|
|
|
|
if (!databaseOpe)//OK
|
|
{
|
|
NSDictionary *snapshot = nil;
|
|
NSArray *classPropertyNames = nil;
|
|
NSArray *dbSnapshotKeys = nil;
|
|
int i = 0;
|
|
int propNamesCount = 0;
|
|
int snapKeyCount = 0;
|
|
NSMutableDictionary *row = nil;
|
|
NSMutableDictionary *newRow = nil;
|
|
EOEntity *entity = nil;
|
|
NSArray *primaryKeyAttributes = nil;
|
|
|
|
entity = [_database entityForObject: object]; //OK
|
|
NSDebugMLLog(@"EODatabaseContext", @"entity name=%@",
|
|
[entity name]);
|
|
|
|
primaryKeyAttributes = [entity primaryKeyAttributes]; //OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"primaryKeyAttributes=%@",
|
|
primaryKeyAttributes);
|
|
|
|
databaseOpe = [EODatabaseOperation
|
|
databaseOperationWithGlobalID: gid
|
|
object: object
|
|
entity: entity]; //OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"CREATED databaseOpe=%@\nfor object %p %@",
|
|
databaseOpe, object, object);
|
|
|
|
snapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid);//OK
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapshot %p=%@",
|
|
snapshot, snapshot);
|
|
|
|
if (!snapshot)
|
|
snapshot = [NSDictionary dictionary];
|
|
|
|
[databaseOpe setDBSnapshot: snapshot];
|
|
NSDebugMLLog(@"EODatabaseContext",@"object=%p databaseOpe=%@",
|
|
object,databaseOpe);
|
|
|
|
classPropertyNames = [entity classPropertyNames]; //OK (code, a3code, numcode, toLabel)
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"classPropertyNames=%@", classPropertyNames);
|
|
|
|
propNamesCount = [classPropertyNames count];
|
|
NSDebugMLLog(@"EODatabaseContext", @"propNamesCount=%d",
|
|
(int)propNamesCount);
|
|
|
|
//TODO: rewrite code: don't use temporary "row"
|
|
row = (NSMutableDictionary*)[NSMutableDictionary dictionary];
|
|
NSDebugMLLog(@"EODatabaseContext",@"object %p (class %@)=%@ ",
|
|
object,[object class],object);
|
|
|
|
/*if ([object isKindOfClass: [EOGenericRecord class]])
|
|
NSDebugMLLog(@"EODatabaseContext", @"dictionary=%@ ",
|
|
[object debugDictionaryDescription]);*/
|
|
if (propNamesCount>0)
|
|
{
|
|
IMP oaiIMP=[classPropertyNames methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < propNamesCount; i++)
|
|
{
|
|
id value = nil;
|
|
NSString *key = GDL2_ObjectAtIndexWithImp(classPropertyNames,oaiIMP,i);
|
|
|
|
|
|
|
|
/*NO !!
|
|
if ([attribute isKindOfClass:[EOAttribute class]] == NO)
|
|
continue;
|
|
// if ([attribute isFlattened] == NO)
|
|
*/
|
|
value = [object storedValueForKey: key]; //OK
|
|
NSDebugMLLog(@"EODatabaseContext", @"key=%@ value=%@",
|
|
key, value);
|
|
|
|
if (!value)
|
|
{
|
|
value = GDL2_EONull;
|
|
|
|
[[[entity attributeNamed: key] validateValue: &value]
|
|
raise];
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"key=%@ value=%@",
|
|
key, value);
|
|
|
|
[row setObject: value
|
|
forKey: key];
|
|
}
|
|
};
|
|
|
|
newRow = [[NSMutableDictionary alloc]
|
|
initWithDictionary: snapshot
|
|
copyItems: NO];
|
|
|
|
|
|
|
|
dbSnapshotKeys = [entity dbSnapshotKeys]; //OK (numcode, code, a3code)
|
|
NSDebugMLLog(@"EODatabaseContext", @"dbSnapshotKeys=%@",
|
|
dbSnapshotKeys);
|
|
|
|
snapKeyCount = [dbSnapshotKeys count];
|
|
|
|
if (snapKeyCount>0)
|
|
{
|
|
IMP oaiIMP=[dbSnapshotKeys methodForSelector: @selector(objectAtIndex:)];
|
|
for (i = 0; i < snapKeyCount; i++)
|
|
{
|
|
id key = GDL2_ObjectAtIndexWithImp(dbSnapshotKeys,oaiIMP,i);
|
|
id value = [row objectForKey: key]; //Really this key ?
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"key=%@ value=%@",
|
|
key, value);
|
|
|
|
// NSAssert1(value,@"No value for %@",key);
|
|
|
|
if (value)
|
|
[newRow setObject: value
|
|
forKey: key];
|
|
}
|
|
};
|
|
|
|
|
|
|
|
[databaseOpe setNewRow: newRow];
|
|
[self recordDatabaseOperation: databaseOpe];
|
|
RELEASE(newRow);
|
|
}
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
|
|
|
|
|
|
return databaseOpe;
|
|
}
|
|
|
|
- (void)relayPrimaryKey: (NSDictionary*)pk
|
|
sourceObject: (id)sourceObject
|
|
destObject: (id)destObject
|
|
relationship: (EORelationship*)relationship
|
|
{
|
|
//OK
|
|
NSDictionary *relayedAttributes = nil;
|
|
EOEntity *destEntity = nil;
|
|
NSArray *destAttributes = nil;
|
|
NSArray *destAttributeNames = nil;
|
|
NSDictionary *keyValues = nil;
|
|
NSArray *values = nil;
|
|
int i, count;
|
|
BOOL nullPKValues = YES;
|
|
|
|
|
|
|
|
NSAssert3(destObject,
|
|
@"No destinationObject. pk=%@ relationship=%@ sourceObject=%@",
|
|
pk,relationship,sourceObject);
|
|
|
|
destAttributes = [relationship destinationAttributes];
|
|
|
|
|
|
destAttributeNames = [destAttributes resultsOfPerformingSelector:
|
|
@selector(name)];
|
|
NSDebugMLLog(@"EODatabaseContext", @"destAttributeNames=%@",
|
|
destAttributeNames);
|
|
|
|
keyValues = [self valuesForKeys: destAttributeNames
|
|
object: destObject];
|
|
|
|
|
|
values = [keyValues allValues];
|
|
|
|
|
|
//Now test if null values
|
|
count = [values count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[values methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; nullPKValues && i < count; i++)
|
|
nullPKValues = _isNilOrEONull(GDL2_ObjectAtIndexWithImp(values,oaiIMP,i));
|
|
};
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"nullPKValues=%s",
|
|
(nullPKValues ? "YES" : "NO"));
|
|
|
|
if (nullPKValues)
|
|
{
|
|
relayedAttributes = [self relayAttributesInRelationship: relationship
|
|
sourceObject: sourceObject
|
|
destinationObject: destObject];
|
|
|
|
destEntity = [relationship destinationEntity];
|
|
|
|
[self relayPrimaryKey: relayedAttributes
|
|
object: destObject
|
|
entity: destEntity];
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)relayPrimaryKey: (NSDictionary*)pk
|
|
object: (id)object
|
|
entity: (EOEntity*)entity
|
|
{
|
|
//TODO finish
|
|
NSArray *relationships = nil;
|
|
NSArray *classPropertyNames = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
NSDictionary *dbSnapshot = nil;
|
|
int i, count;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
relationships = [entity relationships]; //OK
|
|
classPropertyNames = [entity classPropertyNames];
|
|
dbOpe = [self databaseOperationForObject: object];
|
|
|
|
|
|
|
|
dbSnapshot = [dbOpe dbSnapshot]; //OK
|
|
|
|
|
|
count = [relationships count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EORelationship *relationship = GDL2_ObjectAtIndexWithImp(relationships,oaiIMP,i);
|
|
EORelationship *substRelationship = nil;
|
|
BOOL propagatesPrimaryKey = NO;
|
|
|
|
|
|
|
|
substRelationship
|
|
= [relationship _substitutionRelationshipForRow: dbSnapshot];
|
|
propagatesPrimaryKey = [substRelationship propagatesPrimaryKey]; //substRelationship or relationship?
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p relationship name=%@ ==> "
|
|
@"propagatesPrimaryKey=%s",
|
|
object,
|
|
[relationship name],
|
|
(propagatesPrimaryKey ? "YES" : "NO"));
|
|
|
|
if (propagatesPrimaryKey)
|
|
{
|
|
NSString *relName = [substRelationship name]; //this one ??
|
|
|
|
|
|
|
|
if ([classPropertyNames containsObject: relName])
|
|
{
|
|
id value = nil;
|
|
id snapshot = nil;
|
|
id snapshotValue = nil;
|
|
BOOL isToMany = NO;
|
|
|
|
value = [object storedValueForKey: relName];
|
|
|
|
|
|
snapshot = [self _currentCommittedSnapshotForObject: object];
|
|
|
|
|
|
snapshotValue = [snapshot objectForKey:relName];//ret nil
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapshotValue=%@",
|
|
snapshotValue);
|
|
|
|
isToMany = [substRelationship isToMany]; //this one ??
|
|
NSDebugMLLog(@"EODatabaseContext", @"isToMany=%s",
|
|
(isToMany ? "YES" : "NO"));
|
|
|
|
if (isToMany)
|
|
{
|
|
int valueValuesCount = 0;
|
|
|
|
value = [((NSArray*)value) shallowCopy];
|
|
valueValuesCount = [value count];
|
|
if (valueValuesCount>0)
|
|
{
|
|
int iValueValue = 0;
|
|
IMP vObjectAtIndexIMP=[value methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (iValueValue = 0;
|
|
iValueValue < valueValuesCount;
|
|
iValueValue++)
|
|
{
|
|
id valueValue = GDL2_ObjectAtIndexWithImp(value,vObjectAtIndexIMP,iValueValue);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"valueValue=%@", valueValue);
|
|
|
|
[self relayPrimaryKey: pk
|
|
sourceObject: object
|
|
destObject: valueValue
|
|
relationship: substRelationship]; //this one ??
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
// 1:1 relationships may be optional so we may have no value here
|
|
if (value)
|
|
{
|
|
[self relayPrimaryKey: pk
|
|
sourceObject: object
|
|
destObject: value
|
|
relationship: substRelationship]; //this one ??
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void) createAdaptorOperationsForDatabaseOperation: (EODatabaseOperation*)dbOpe
|
|
attributes: (NSArray*)attributes
|
|
{
|
|
//NEAR OK
|
|
BOOL isSomethingTodo = YES;
|
|
EOEntity *entity = nil;
|
|
EODatabaseOperator dbOperator = EODatabaseNothingOperator;
|
|
NSDictionary *changedValues = nil;
|
|
|
|
|
|
|
|
|
|
NSAssert(dbOpe, @"No operation");
|
|
|
|
entity = [dbOpe entity]; //OK
|
|
dbOperator = [dbOpe databaseOperator]; //OK
|
|
|
|
|
|
|
|
|
|
switch (dbOperator)
|
|
{
|
|
case EODatabaseUpdateOperator:
|
|
{
|
|
changedValues = [dbOpe rowDiffsForAttributes:attributes];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"changedValues %p=%@",
|
|
changedValues, changedValues);
|
|
|
|
if ([changedValues count] == 0)
|
|
isSomethingTodo = NO;
|
|
else
|
|
{
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EODatabaseInsertOperator:
|
|
{
|
|
changedValues = [dbOpe newRow]; //OK
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"changedValues %p=%@",
|
|
changedValues, changedValues);
|
|
}
|
|
break;
|
|
|
|
case EODatabaseDeleteOperator:
|
|
{
|
|
isSomethingTodo = YES;
|
|
}
|
|
break;
|
|
|
|
case EODatabaseNothingOperator:
|
|
{
|
|
//Nothing!
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
NSEmitTODO();
|
|
// [self notImplemented:_cmd]; //TODO
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (isSomethingTodo)
|
|
{
|
|
EOAdaptorOperation *adaptorOpe = nil;
|
|
NSString *procedureOpeName = nil;
|
|
EOAdaptorOperator adaptorOperator = EOAdaptorUndefinedOperator;
|
|
EOStoredProcedure *storedProcedure = nil;
|
|
|
|
NSDictionary *valuesToWrite = nil;
|
|
EOQualifier *lockingQualifier = nil;
|
|
|
|
switch (dbOperator)
|
|
{
|
|
case EODatabaseUpdateOperator:
|
|
case EODatabaseDeleteOperator:
|
|
{
|
|
NSArray *pkAttributes;
|
|
NSArray *lockingAttributes;
|
|
NSDictionary *dbSnapshot;
|
|
|
|
pkAttributes = [self primaryKeyAttributesForAttributes: attributes
|
|
entity: entity];
|
|
lockingAttributes = [self lockingAttributesForAttributes:
|
|
attributes
|
|
entity: entity];
|
|
|
|
dbSnapshot = [dbOpe dbSnapshot];
|
|
|
|
lockingQualifier = [self qualifierForLockingAttributes:
|
|
lockingAttributes
|
|
primaryKeyAttributes: pkAttributes
|
|
entity: entity
|
|
snapshot: dbSnapshot];
|
|
|
|
NSEmitTODO();
|
|
|
|
//TODO=self lockingNonQualifiableAttributes:##### ret nil
|
|
NSDebugMLLog(@"EODatabaseContext", @"lockingQualifier=%@",
|
|
lockingQualifier);
|
|
|
|
/*MIRKO for UPDATE:
|
|
//TODO-NOW
|
|
{
|
|
if ([self isObjectLockedWithGlobalID:gid] == NO)
|
|
{
|
|
EOAdaptorOperation *lockOperation;
|
|
EOQualifier *qualifier;
|
|
EOAttribute *attribute;
|
|
NSEnumerator *attrsEnum;
|
|
NSArray *attrsUsedForLocking, *primaryKeyAttributes;
|
|
NSMutableDictionary *qualifierSnapshot, *lockSnapshot;
|
|
NSMutableArray *lockAttributes;
|
|
|
|
lockOperation = [EOAdaptorOperation adaptorOperationWithEntity:
|
|
entity];
|
|
|
|
attrsUsedForLocking = [entity attributesUsedForLocking];
|
|
primaryKeyAttributes = [entity primaryKeyAttributes];
|
|
|
|
qualifierSnapshot = [NSMutableDictionary
|
|
dictionaryWithCapacity:16];
|
|
lockSnapshot = [NSMutableDictionary dictionaryWithCapacity:8];
|
|
lockAttributes = [NSMutableArray arrayWithCapacity:8];
|
|
|
|
attrsEnum = [primaryKeyAttributes objectEnumerator];
|
|
while ((attribute = [attrsEnum nextObject]))
|
|
{
|
|
NSString *name = [attribute name];
|
|
|
|
[lockSnapshot setObject:[snapshot objectForKey:name]
|
|
forKey:name];
|
|
}
|
|
|
|
|
|
|
|
attrsEnum = [attrsUsedForLocking objectEnumerator];
|
|
while ((attribute = [attrsEnum nextObject]))
|
|
{
|
|
NSString *name = [attribute name];
|
|
|
|
if ([primaryKeyAttributes containsObject:attribute] == NO)
|
|
{
|
|
if ([attribute adaptorValueType] == EOAdaptorBytesType)
|
|
{
|
|
[lockAttributes addObject:attribute];
|
|
[lockSnapshot setObject:[snapshot
|
|
objectForKey:name]
|
|
forKey:name];
|
|
}
|
|
else
|
|
[qualifierSnapshot setObject:[snapshot
|
|
objectForKey:name]
|
|
forKey:name];
|
|
}
|
|
}
|
|
|
|
|
|
qualifier = AUTORELEASE([[EOAndQualifier alloc]
|
|
initWithQualifiers:
|
|
[entity qualifierForPrimaryKey:
|
|
[entity primaryKeyForGlobalID:
|
|
(EOKeyGlobalID *)gid]],
|
|
[EOQualifier qualifierToMatchAllValues:
|
|
qualifierSnapshot],
|
|
nil]);
|
|
|
|
if ([lockAttributes count] == 0)
|
|
lockAttributes = nil;
|
|
if ([lockSnapshot count] == 0)
|
|
lockSnapshot = nil;
|
|
|
|
[lockOperation setAdaptorOperator:EOAdaptorLockOperator];
|
|
[lockOperation setQualifier:qualifier];
|
|
[lockOperation setAttributes:lockAttributes];
|
|
[lockOperation setChangedValues:lockSnapshot];
|
|
|
|
|
|
[op addAdaptorOperation:lockOperation];
|
|
}
|
|
*/
|
|
}
|
|
break;
|
|
|
|
case EODatabaseInsertOperator:
|
|
break;
|
|
|
|
case EODatabaseNothingOperator:
|
|
break;
|
|
}
|
|
|
|
adaptorOpe = [EOAdaptorOperation adaptorOperationWithEntity: entity];
|
|
|
|
|
|
|
|
switch (dbOperator)
|
|
{
|
|
case EODatabaseInsertOperator:
|
|
procedureOpeName = @"EOInsertProcedure";
|
|
adaptorOperator = EOAdaptorInsertOperator;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"changedValues %p=%@",
|
|
changedValues, changedValues);
|
|
|
|
valuesToWrite = [self valuesToWriteForAttributes: attributes
|
|
entity: entity
|
|
changedValues: changedValues];
|
|
break;
|
|
|
|
case EODatabaseUpdateOperator:
|
|
procedureOpeName = @"EOUpdateProcedure";
|
|
adaptorOperator = EOAdaptorUpdateOperator;
|
|
valuesToWrite = [self valuesToWriteForAttributes: attributes
|
|
entity: entity
|
|
changedValues: changedValues];
|
|
break;
|
|
|
|
case EODatabaseDeleteOperator:
|
|
procedureOpeName = @"EODeleteProcedure";
|
|
adaptorOperator = EOAdaptorDeleteOperator;
|
|
/*
|
|
MIRKO
|
|
NSMutableArray *newKeys = AUTORELEASE([[NSMutableArray alloc]
|
|
initWithCapacity:count]);
|
|
NSMutableArray *newVals = AUTORELEASE([[NSMutableArray alloc]
|
|
initWithCapacity:count]);
|
|
|
|
if ([entity isReadOnly] == YES)
|
|
{
|
|
[NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: cannot delete object for readonly entity %@", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, [entity name]];
|
|
}
|
|
|
|
[aOp setAdaptorOperator:EOAdaptorDeleteOperator];
|
|
|
|
count = [primaryKeys count];
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOAttribute *attribute = [primaryKeys objectAtIndex:i];
|
|
NSString *key = [attribute name];
|
|
id val;
|
|
if ([attribute isFlattened] == NO)
|
|
{
|
|
// Turbocat
|
|
//val = [object storedValueForKey:key];
|
|
if (currentSnapshot) {
|
|
val = [currentSnapshot objectForKey:key];
|
|
}
|
|
|
|
if (!val) {
|
|
[NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: cannot delete object (snapshot) '%@' for unkown primarykey value '%@'", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, currentSnapshot, key];
|
|
}
|
|
|
|
if (val == nil)
|
|
val = GDL2_EONull;
|
|
|
|
[newKeys addObject:key];
|
|
[newVals addObject:val];
|
|
}
|
|
}
|
|
|
|
row = [NSDictionary dictionaryWithObjects:newVals
|
|
forKeys:newKeys];
|
|
|
|
[aOp setQualifier:[entity qualifierForPrimaryKey:[op newRow]]];
|
|
|
|
==>NO? in _commitTransaction [self forgetSnapshotForGlobalID:[op globalID]];
|
|
*/
|
|
break;
|
|
|
|
case EODatabaseNothingOperator:
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"Db Ope %@ for Nothing !!!", dbOpe);
|
|
//Nothing?
|
|
break;
|
|
|
|
default:
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
// only for insert ??
|
|
storedProcedure = [entity storedProcedureForOperation: procedureOpeName];
|
|
if (storedProcedure)
|
|
{
|
|
adaptorOperator = EOAdaptorStoredProcedureOperator;
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"adaptorOperator=%d",
|
|
adaptorOperator);
|
|
|
|
|
|
if (adaptorOpe)
|
|
{
|
|
[adaptorOpe setAdaptorOperator: adaptorOperator];
|
|
|
|
|
|
if (valuesToWrite)
|
|
[adaptorOpe setChangedValues: valuesToWrite];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"lockingQualifier=%@",
|
|
lockingQualifier);
|
|
|
|
if (lockingQualifier)
|
|
[adaptorOpe setQualifier: lockingQualifier];
|
|
|
|
[dbOpe addAdaptorOperation: adaptorOpe];
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void) createAdaptorOperationsForDatabaseOperation: (EODatabaseOperation*)dbOpe
|
|
{
|
|
//OK for Update - Test for others
|
|
NSArray *attributesToSave = nil;
|
|
NSMutableArray *attributes = nil;
|
|
int count=0;
|
|
EODatabaseOperator dbOperator = EODatabaseNothingOperator;
|
|
EOEntity *entity = [dbOpe entity]; //OK
|
|
NSDictionary *rowDiffs = nil;
|
|
|
|
|
|
|
|
[self processSnapshotForDatabaseOperation: dbOpe]; //OK
|
|
dbOperator = [dbOpe databaseOperator]; //OK
|
|
|
|
if (dbOperator == EODatabaseUpdateOperator) //OK
|
|
{
|
|
rowDiffs = [dbOpe rowDiffs];
|
|
|
|
}
|
|
|
|
attributesToSave = [entity _attributesToSave]; //OK for update, OK for insert
|
|
attributes = [NSMutableArray array];
|
|
|
|
count = [attributesToSave count];
|
|
if (count>0)
|
|
{
|
|
int i=0;
|
|
IMP attributesAddObjectIMP=[attributes methodForSelector:@selector(addObject:)];
|
|
IMP attributesToSaveObjectAtIndexIMP=[attributesToSave methodForSelector:@selector(objectAtIndex:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOAttribute *attribute =
|
|
GDL2_ObjectAtIndexWithImp(attributesToSave,attributesToSaveObjectAtIndexIMP,i);
|
|
|
|
|
|
|
|
if (![attribute isFlattened] && ![attribute isDerived]) //VERIFY
|
|
{
|
|
GDL2_AddObjectWithImp(attributes,attributesAddObjectIMP,attribute);
|
|
|
|
if ([rowDiffs objectForKey: [attribute name]]
|
|
&& [attribute isReadOnly])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO: excption ???
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
[self createAdaptorOperationsForDatabaseOperation: dbOpe
|
|
attributes: attributes];
|
|
}
|
|
|
|
- (NSArray*) orderAdaptorOperations
|
|
{
|
|
//seems OK
|
|
NSMutableArray *orderedAdaptorOpe = (NSMutableArray*)[NSMutableArray array];
|
|
|
|
|
|
|
|
//MIRKO
|
|
if (_delegateRespondsTo.willOrderAdaptorOperations == YES)
|
|
orderedAdaptorOpe = (NSMutableArray*)
|
|
[_delegate databaseContext: self
|
|
willOrderAdaptorOperationsFromDatabaseOperations:
|
|
NSAllMapTableValues(_dbOperationsByGlobalID)];
|
|
else
|
|
{
|
|
NSArray *entities = nil;
|
|
NSMutableArray *adaptorOperations = [NSMutableArray array];
|
|
NSMapEnumerator dbOpeEnum;
|
|
EOGlobalID *gid = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
NSHashTable *entitiesHashTable = NSCreateHashTable(NSNonOwnedPointerHashCallBacks,32);
|
|
|
|
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
|
|
|
|
while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid,
|
|
(void **)&dbOpe))
|
|
{
|
|
NSArray *dbOpeAdaptorOperations = [dbOpe adaptorOperations];
|
|
int count = [dbOpeAdaptorOperations count];
|
|
|
|
|
|
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[dbOpeAdaptorOperations methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOAdaptorOperation *adaptorOpe = GDL2_ObjectAtIndexWithImp(dbOpeAdaptorOperations,oaiIMP,i);
|
|
EOEntity *entity = nil;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"adaptorOpe=%@",
|
|
adaptorOpe);
|
|
|
|
[adaptorOperations addObject: adaptorOpe];
|
|
entity = [adaptorOpe entity];
|
|
|
|
|
|
NSHashInsertIfAbsent(entitiesHashTable, entity);
|
|
}
|
|
};
|
|
}
|
|
|
|
entities = NSAllHashTableObjects(entitiesHashTable);
|
|
NSFreeHashTable(entitiesHashTable);
|
|
|
|
entitiesHashTable = NULL;
|
|
|
|
|
|
{
|
|
NSArray *entityNameOrderingArray = [self entityNameOrderingArrayForEntities:entities];
|
|
int iAdaptoOpe = 0;
|
|
int adaptorOpeCount = [adaptorOperations count];
|
|
int entitiesCount = [entityNameOrderingArray count];
|
|
|
|
if (entitiesCount>0)
|
|
{
|
|
IMP entityObjectAtIndexIMP=[entityNameOrderingArray methodForSelector: @selector(objectAtIndex:)];
|
|
IMP opeObjectAtIndexIMP=[adaptorOperations methodForSelector: @selector(objectAtIndex:)];
|
|
int iEntity=0;
|
|
|
|
for (iEntity = 0; iEntity < entitiesCount; iEntity++)
|
|
{
|
|
EOEntity *entity = GDL2_ObjectAtIndexWithImp(entityNameOrderingArray,entityObjectAtIndexIMP,iEntity);
|
|
|
|
|
|
|
|
for (iAdaptoOpe = 0; iAdaptoOpe < adaptorOpeCount; iAdaptoOpe++)
|
|
{
|
|
EOAdaptorOperation *adaptorOpe = GDL2_ObjectAtIndexWithImp(adaptorOperations,opeObjectAtIndexIMP,iAdaptoOpe);
|
|
EOEntity *opeEntity = [adaptorOpe entity];
|
|
|
|
if (opeEntity == entity)
|
|
[orderedAdaptorOpe addObject: adaptorOpe];
|
|
}
|
|
}
|
|
};
|
|
|
|
NSAssert2([orderedAdaptorOpe count] == adaptorOpeCount,
|
|
@"Different ordered (%d) an unordered adaptor operations count (%d)",
|
|
[orderedAdaptorOpe count],
|
|
adaptorOpeCount);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return orderedAdaptorOpe;
|
|
}
|
|
|
|
- (NSArray*) entitiesOnWhichThisEntityDepends: (EOEntity*)entity
|
|
{
|
|
NSMutableArray *entities = nil;
|
|
NSArray *relationships = nil;
|
|
int count;
|
|
|
|
|
|
|
|
relationships = [entity relationships];
|
|
count = [relationships count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EORelationship *relationship = GDL2_ObjectAtIndexWithImp(relationships,oaiIMP,i);
|
|
|
|
|
|
|
|
if (![relationship isToMany]) //If to many: do nothing
|
|
{
|
|
if ([relationship isFlattened])
|
|
{
|
|
//TODO VERIFY
|
|
EOExpressionArray *definitionArray=[relationship _definitionArray];
|
|
EORelationship *firstRelationship=[definitionArray objectAtIndex:0];
|
|
EOEntity *firstDefEntity=[firstRelationship destinationEntity];
|
|
NSArray *defDependEntities=[self
|
|
entitiesOnWhichThisEntityDepends:firstDefEntity];
|
|
if ([defDependEntities count]>0)
|
|
{
|
|
if (!entities)
|
|
entities = [NSMutableArray array];
|
|
|
|
[entities addObjectsFromArray: defDependEntities];
|
|
};
|
|
}
|
|
else
|
|
{
|
|
//Here ??
|
|
EOEntity *destinationEntity = [relationship destinationEntity];
|
|
EORelationship *inverseRelationship = [relationship
|
|
anyInverseRelationship];
|
|
|
|
if ([inverseRelationship isToMany])
|
|
{
|
|
//Do nothing ?
|
|
}
|
|
else
|
|
{
|
|
if ([inverseRelationship propagatesPrimaryKey])
|
|
{
|
|
//OK
|
|
if (!entities)
|
|
entities = [NSMutableArray array];
|
|
|
|
[entities addObject: destinationEntity];
|
|
}
|
|
else
|
|
{
|
|
if ([inverseRelationship ownsDestination])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return entities;
|
|
}
|
|
|
|
- (NSArray*)entityNameOrderingArrayForEntities: (NSArray*)entities
|
|
{
|
|
//TODO
|
|
NSMutableArray *ordering = [NSMutableArray array];
|
|
NSMutableSet *orderedEntities = [NSMutableSet set];
|
|
/*EODatabase *database = [self database];
|
|
NSArray *models = [database models];*/
|
|
NSMutableDictionary *dependsDict = [NSMutableDictionary dictionary];
|
|
int count = [entities count];
|
|
|
|
//TODO NSArray* originalOrdering=...
|
|
/*TODO for each mdoel:
|
|
userInfo (ret nil)
|
|
*/
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[entities methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
//OK
|
|
EOEntity *entity=GDL2_ObjectAtIndexWithImp(entities,oaiIMP,i);
|
|
NSArray *dependsEntities = [self
|
|
entitiesOnWhichThisEntityDepends: entity];
|
|
|
|
if ([dependsEntities count])
|
|
[dependsDict setObject: dependsEntities
|
|
forKey: [entity name]];
|
|
}
|
|
|
|
ordering = [NSMutableArray array];
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOEntity *entity=GDL2_ObjectAtIndexWithImp(entities,oaiIMP,i);
|
|
[self insertEntity: entity
|
|
intoOrderingArray: ordering
|
|
withDependencies: dependsDict
|
|
processingSet: orderedEntities];
|
|
}
|
|
}
|
|
//TODO
|
|
/*
|
|
model userInfo //ret nil
|
|
setUserInfo: {EOEntityOrdering = ordering; }
|
|
*/
|
|
|
|
return ordering;
|
|
}
|
|
|
|
- (BOOL) isValidQualifierTypeForAttribute: (EOAttribute*)attribute
|
|
{
|
|
//OK
|
|
BOOL isValid = NO;
|
|
EOEntity *entity = nil;
|
|
EOModel *model = nil;
|
|
EODatabase *database = nil;
|
|
EOAdaptor *adaptor = nil;
|
|
NSString *externalType = nil;
|
|
|
|
|
|
|
|
entity = [attribute entity];
|
|
|
|
NSAssert1(entity, @"No entity for attribute %@", attribute);
|
|
|
|
model = [entity model];
|
|
database = [self database];
|
|
adaptor = [database adaptor];
|
|
externalType = [attribute externalType];
|
|
isValid = [adaptor isValidQualifierType: externalType
|
|
model: model];
|
|
|
|
//TODO REMOVE
|
|
if (!isValid)
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return isValid;
|
|
}
|
|
|
|
- (id) lockingNonQualifiableAttributes: (NSArray*)attributes
|
|
{
|
|
//TODO finish
|
|
EOEntity *entity = nil;
|
|
NSArray *attributesUsedForLocking = nil;
|
|
int count = 0;
|
|
|
|
count = [attributes count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[attributes methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id attribute = GDL2_ObjectAtIndexWithImp(attributes,oaiIMP,i);
|
|
|
|
if (!entity)
|
|
{
|
|
entity = [attribute entity];
|
|
attributesUsedForLocking = [entity attributesUsedForLocking];
|
|
}
|
|
|
|
if (![self isValidQualifierTypeForAttribute: attribute])
|
|
{
|
|
NSEmitTODO();
|
|
// [self notImplemented:_cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSEmitTODO();
|
|
//Nothing ??
|
|
// [self notImplemented:_cmd]; //TODO ??
|
|
}
|
|
}
|
|
};
|
|
|
|
return nil;//??
|
|
}
|
|
|
|
- (NSArray*) lockingAttributesForAttributes: (NSArray*)attributes
|
|
entity: (EOEntity*)entity
|
|
{
|
|
//TODO
|
|
NSArray *retAttributes = nil;
|
|
int count = 0;
|
|
NSArray *attributesUsedForLocking = nil;
|
|
|
|
|
|
|
|
attributesUsedForLocking = [entity attributesUsedForLocking];
|
|
count = [attributes count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[attributes methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id attribute = GDL2_ObjectAtIndexWithImp(attributes,oaiIMP,i);
|
|
//do this on 1st only
|
|
BOOL isFlattened = [attribute isFlattened];
|
|
|
|
if (isFlattened)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSArray *rootAttributesUsedForLocking = [entity rootAttributesUsedForLocking];
|
|
|
|
retAttributes = rootAttributesUsedForLocking;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
return retAttributes; //TODO
|
|
}
|
|
|
|
- (NSArray*) primaryKeyAttributesForAttributes: (NSArray*)attributes
|
|
entity: (EOEntity*)entity
|
|
{
|
|
//TODO
|
|
NSArray *retAttributes = nil;
|
|
int count = 0;
|
|
|
|
|
|
//TODO
|
|
|
|
count = [attributes count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[attributes methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id attribute = GDL2_ObjectAtIndexWithImp(attributes,oaiIMP,i);
|
|
BOOL isFlattened = [attribute isFlattened];
|
|
|
|
//call isFlattened on 1st only
|
|
if (isFlattened)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSArray *primaryKeyAttributes = [entity primaryKeyAttributes];
|
|
|
|
retAttributes = primaryKeyAttributes;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
return retAttributes;
|
|
}
|
|
|
|
- (EOQualifier*) qualifierForLockingAttributes: (NSArray*)attributes
|
|
primaryKeyAttributes: (NSArray*)primaryKeyAttributes
|
|
entity: (EOEntity*)entity
|
|
snapshot: (NSDictionary*)snapshot
|
|
{
|
|
//OK
|
|
EOQualifier *qualifier = nil;
|
|
NSMutableArray *qualifiers = nil;
|
|
int which;
|
|
|
|
//First use primaryKeyAttributes, next use attributes
|
|
for (which = 0; which < 2; which++)
|
|
{
|
|
NSArray *array = (which == 0 ? primaryKeyAttributes : attributes);
|
|
NSUInteger i,count = [array count];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOAttribute *attribute = [array objectAtIndex: i];
|
|
|
|
if (which == 0 || ![primaryKeyAttributes containsObject: attribute])// Test if we haven't already processed it
|
|
{
|
|
if (![self isValidQualifierTypeForAttribute: attribute])
|
|
{
|
|
NSLog(@"Invalid externalType for attribute '%@' of entity named '%@' - model '%@'",
|
|
[attribute name], [[attribute entity] name],
|
|
[[[attribute entity] model] name]);
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSString *attributeName = nil;
|
|
NSString *snapName = nil;
|
|
id value = nil;
|
|
EOQualifier *aQualifier = nil;
|
|
|
|
attributeName = [attribute name];
|
|
NSAssert1(attributeName, @"no attribute name for attribute %@", attribute);
|
|
|
|
snapName = [entity snapshotKeyForAttributeName: attributeName];
|
|
NSAssert2(snapName, @"no snapName for attribute %@ in entity %@",
|
|
attributeName, [entity name]);
|
|
|
|
value = [snapshot objectForKey:snapName];
|
|
|
|
NSAssert4(value != nil,
|
|
@"no value for snapshotKey '%@' in snapshot (address=%p) %@ for entity %@",
|
|
snapName, snapshot, snapshot, [entity name]);
|
|
|
|
aQualifier
|
|
= [EOKeyValueQualifier qualifierWithKey: attributeName
|
|
operatorSelector: @selector(isEqualTo:)
|
|
value: value];
|
|
|
|
if (!qualifiers)
|
|
qualifiers = [NSMutableArray array];
|
|
|
|
[qualifiers addObject: aQualifier];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ([qualifiers count] == 1)
|
|
{
|
|
qualifier = [qualifiers objectAtIndex: 0];
|
|
}
|
|
else
|
|
{
|
|
qualifier = [EOAndQualifier qualifierWithQualifierArray: qualifiers];
|
|
}
|
|
|
|
return qualifier;
|
|
}
|
|
|
|
- (void) insertEntity: (EOEntity*)entity
|
|
intoOrderingArray: (NSMutableArray*)orderingArray
|
|
withDependencies: (NSDictionary*)dependencies
|
|
processingSet: (NSMutableSet*)processingSet
|
|
{
|
|
//TODO: manage dependencies {CustomerCredit = (<EOEntity name: Customer>); }
|
|
// and processingSet
|
|
[orderingArray addObject: entity];
|
|
[processingSet addObject: [entity name]];
|
|
}
|
|
|
|
|
|
- (void) processSnapshotForDatabaseOperation: (EODatabaseOperation*)dbOpe
|
|
{
|
|
//Near OK
|
|
EOAdaptor *adaptor = [_database adaptor];//OK
|
|
EOEntity *entity = [dbOpe entity];//OK
|
|
NSDictionary *newRow = nil;
|
|
NSDictionary *dbSnapshot = nil;
|
|
NSEnumerator *attrNameEnum = nil;
|
|
id attrName = nil;
|
|
IMP enumNO=NULL; // nextObject
|
|
|
|
|
|
|
|
|
|
|
|
newRow = [dbOpe newRow]; //OK{a3code = Q77; code = Q7; numcode = 007; } //ALLOK
|
|
|
|
|
|
dbSnapshot = [dbOpe dbSnapshot];
|
|
NSDebugMLLog(@"EODatabaseContext", @"dbSnapshot %p=%@",
|
|
dbSnapshot, dbSnapshot);
|
|
|
|
attrNameEnum = [newRow keyEnumerator];
|
|
enumNO=NULL;
|
|
while ((attrName = GDL2_NextObjectWithImpPtr(attrNameEnum,&enumNO)))
|
|
{
|
|
EOAttribute *attribute = [entity attributeNamed: attrName];
|
|
id newRowValue = nil;
|
|
id dbSnapshotValue = nil;
|
|
|
|
|
|
|
|
newRowValue = [newRow objectForKey:attrName];
|
|
|
|
|
|
dbSnapshotValue = [dbSnapshot objectForKey: attrName];
|
|
NSDebugMLLog(@"EODatabaseContext", @"dbSnapshotValue=%@",
|
|
dbSnapshotValue);
|
|
|
|
if (dbSnapshotValue && ![newRowValue isEqual: dbSnapshotValue])
|
|
{
|
|
id adaptorValue = [adaptor fetchedValueForValue: newRowValue
|
|
attribute: attribute]; //this call is OK
|
|
|
|
|
|
//TODO-NOW SO WHAT ?? may be replacing newRow diff values by adaptorValue if different ????
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- (NSDictionary*) valuesToWriteForAttributes: (NSArray*)attributes
|
|
entity: (EOEntity*)entity
|
|
changedValues: (NSDictionary*)changedValues
|
|
{
|
|
//NEAR OK
|
|
NSMutableDictionary *valuesToWrite = [NSMutableDictionary dictionary];
|
|
BOOL isReadOnlyEntity = NO;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isReadOnlyEntity = [entity isReadOnly];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"isReadOnlyEntity=%s",
|
|
(isReadOnlyEntity ? "YES" : "NO"));
|
|
|
|
if (isReadOnlyEntity)
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
int count = [attributes count];
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[attributes methodForSelector: @selector(objectAtIndex:)];
|
|
int i=0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
EOAttribute *attribute = GDL2_ObjectAtIndexWithImp(attributes,oaiIMP,i);
|
|
BOOL isReadOnly = [attribute isReadOnly];
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"isReadOnly=%s",
|
|
(isReadOnly ? "YES" : "NO"));
|
|
|
|
if (isReadOnly)
|
|
{
|
|
NSEmitTODO();
|
|
NSDebugMLog(@"attribute=%@", attribute);
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
else
|
|
{
|
|
NSString *attrName = [attribute name];
|
|
NSString *snapName = nil;
|
|
id value = nil;
|
|
|
|
|
|
|
|
snapName = [entity snapshotKeyForAttributeName: attrName];
|
|
|
|
|
|
value = [changedValues objectForKey: snapName];
|
|
|
|
|
|
if (value)
|
|
[valuesToWrite setObject: value
|
|
forKey: attrName];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return valuesToWrite;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EODatabaseContext(EOBatchFaulting)
|
|
|
|
- (void)batchFetchRelationship: (EORelationship *)relationship
|
|
forSourceObjects: (NSArray *)objects
|
|
editingContext: (EOEditingContext *)editingContext
|
|
{ // TODO
|
|
NSMutableArray *qualifierArray, *valuesArray, *toManySnapshotArray;
|
|
NSMutableDictionary *values;
|
|
NSArray *array;
|
|
NSEnumerator *objsEnum, *joinsEnum, *keyEnum;
|
|
NSString *key;
|
|
EOFetchSpecification *fetch;
|
|
EOQualifier *qualifier;
|
|
EOFault *fault;
|
|
EOJoin *join;
|
|
BOOL equal;
|
|
int i, count;
|
|
id object;
|
|
NSString* relationshipName = nil;
|
|
IMP globalIDForObjectIMP=NULL;
|
|
IMP toManySnapArrayObjectAtIndexIMP=NULL;
|
|
IMP objsEnumNO=NULL;
|
|
IMP objectsOAI=NULL;
|
|
|
|
qualifierArray = AUTORELEASE([GDL2_alloc(NSMutableArray) init]);
|
|
valuesArray = AUTORELEASE([GDL2_alloc(NSMutableArray) init]);
|
|
toManySnapshotArray = AUTORELEASE([GDL2_alloc(NSMutableArray) init]);
|
|
toManySnapArrayObjectAtIndexIMP=[toManySnapshotArray methodForSelector: @selector(objectAtIndex:)];
|
|
relationshipName = [relationship name];
|
|
|
|
objsEnum = [objects objectEnumerator];
|
|
objsEnumNO=NULL;
|
|
while ((object = GDL2_NextObjectWithImpPtr(objsEnum,&objsEnumNO)))
|
|
{
|
|
IMP joinsEnumNO=NO;
|
|
values
|
|
= AUTORELEASE([GDL2_alloc(NSMutableDictionary) initWithCapacity: 4]);
|
|
|
|
fault = [object valueForKey: relationshipName];
|
|
[EOFault clearFault: fault];
|
|
|
|
joinsEnum = [[relationship joins] objectEnumerator];
|
|
while ((join = GDL2_NextObjectWithImpPtr(joinsEnum,&joinsEnumNO)))
|
|
{
|
|
[values setObject: [object valueForKey: [[join sourceAttribute] name]]
|
|
forKey: [[join destinationAttribute] name]];
|
|
}
|
|
|
|
[valuesArray addObject: values];
|
|
[toManySnapshotArray addObject:
|
|
AUTORELEASE([GDL2_alloc(NSMutableArray) init])];
|
|
|
|
[qualifierArray addObject: [EOQualifier qualifierToMatchAllValues:
|
|
values]];
|
|
}
|
|
|
|
if ([qualifierArray count] == 1)
|
|
qualifier = [qualifierArray objectAtIndex: 0];
|
|
else
|
|
qualifier = [EOOrQualifier qualifierWithQualifierArray: qualifierArray];
|
|
|
|
fetch = [EOFetchSpecification fetchSpecificationWithEntityName:
|
|
[[relationship destinationEntity] name]
|
|
qualifier: qualifier
|
|
sortOrderings: nil];
|
|
|
|
array = [self objectsWithFetchSpecification: fetch
|
|
editingContext: editingContext];
|
|
|
|
count = [valuesArray count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[valuesArray methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
objsEnum = [array objectEnumerator];
|
|
objsEnumNO=NULL;
|
|
while ((object = GDL2_NextObjectWithImpPtr(objsEnum,&objsEnumNO)))
|
|
{
|
|
IMP objectVFK=NULL; // valueForKey:
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
IMP keyEnumNO=NULL; // nextObject
|
|
IMP valuesOFK=NULL; // objectForKey:
|
|
equal = YES;
|
|
values = GDL2_ObjectAtIndexWithImp(valuesArray,oaiIMP,i);
|
|
|
|
keyEnum = [values keyEnumerator];
|
|
while ((key = GDL2_NextObjectWithImpPtr(keyEnum,&keyEnumNO)))
|
|
{
|
|
if ([GDL2_ValueForKeyWithImpPtr(object,&objectVFK,key)
|
|
isEqual: GDL2_ObjectForKeyWithImpPtr(values,&valuesOFK,key)] == NO)
|
|
{
|
|
equal = NO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (equal == YES)
|
|
{
|
|
EOGlobalID* gid = nil;
|
|
id snapshot = GDL2_ObjectAtIndexWithImp(toManySnapshotArray,toManySnapArrayObjectAtIndexIMP,i);
|
|
|
|
[[GDL2_ObjectAtIndexWithImpPtr(objects,&objectsOAI,i) valueForKey: relationshipName]
|
|
addObject: object];
|
|
|
|
gid=EOEditingContext_globalIDForObjectWithImpPtr(editingContext,&globalIDForObjectIMP,object);
|
|
|
|
[snapshot addObject: gid];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==> see _registerSnapshot:forSourceGlobalID:relationshipName:editingContext:
|
|
|
|
if (count>0)
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id snapshot = GDL2_ObjectAtIndexWithImp(toManySnapshotArray,toManySnapArrayObjectAtIndexIMP,i);
|
|
EOGlobalID* gid=EOEditingContext_globalIDForObjectWithImpPtr(editingContext,
|
|
&globalIDForObjectIMP,
|
|
GDL2_ObjectAtIndexWithImpPtr(objects,&objectsOAI,i));
|
|
[_database recordSnapshot: snapshot
|
|
forSourceGlobalID: gid
|
|
relationshipName: relationshipName];
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation EODatabaseContext (EODatabaseContextPrivate)
|
|
|
|
- (void) _fireArrayFault: (id)object
|
|
{
|
|
//OK ??
|
|
BOOL fetchIt = YES;
|
|
|
|
|
|
|
|
|
|
|
|
if (_delegateRespondsTo.shouldFetchObjectFault == YES)
|
|
fetchIt = [_delegate databaseContext: self
|
|
shouldFetchObjectFault: object];
|
|
|
|
if (fetchIt)
|
|
{
|
|
/*Class targetClass = Nil;
|
|
void *extraData = NULL;*/
|
|
EOAccessArrayFaultHandler *handler = (EOAccessArrayFaultHandler *)[EOFault handlerForFault:object];
|
|
EOEditingContext *context = [handler editingContext];
|
|
NSString *relationshipName= [handler relationshipName];
|
|
EOKeyGlobalID *gid = [handler sourceGlobalID];
|
|
NSArray *objects = nil;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relationshipName=%@",
|
|
relationshipName);
|
|
|
|
|
|
objects = [context objectsForSourceGlobalID: gid
|
|
relationshipName: relationshipName
|
|
editingContext: context];
|
|
|
|
[EOFault clearFault: object]; //??
|
|
/* in clearFault
|
|
[handler faultWillFire:object];
|
|
targetClass=[handler targetClass];
|
|
extraData=[handler extraData];
|
|
RELEASE(handler);
|
|
*/
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"NEAR FINISHED 1 object count=%d %p %@",
|
|
[object count],
|
|
object,
|
|
object);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"NEAR FINISHED 1 objects count=%d %p %@",
|
|
[objects count],
|
|
objects,
|
|
objects);
|
|
|
|
if (objects != object)
|
|
{
|
|
//No, not needed [object removeObjectsInArray:objects];//Because some objects may be here. We don't want duplicate. It's a hack because I don't see why there's objects in object !
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"NEAR FINISHED 1 object count=%d %p %@",
|
|
[object count],
|
|
object,
|
|
object);
|
|
|
|
[object addObjectsFromArray: objects];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"NEAR FINISHED 2 object count=%d %@",
|
|
[object count],
|
|
object);
|
|
}
|
|
}
|
|
//END!
|
|
/*
|
|
}
|
|
|
|
- (void)_batchToMany:(id)fault
|
|
withHandler:(EOAccessArrayFaultHandler *)handler
|
|
{
|
|
*/
|
|
|
|
/*
|
|
EOAccessArrayFaultHandler *usedHandler, *firstHandler, *lastHandler;
|
|
EOAccessArrayFaultHandler *bufHandler;
|
|
NSMutableDictionary *batchBuffer;
|
|
EOEditingContext *context;
|
|
NSMutableArray *objects;
|
|
EOKeyGlobalID *gid;
|
|
EOEntity *entity;
|
|
EORelationship *relationship;
|
|
unsigned int maxBatch;
|
|
BOOL batch = YES, changeBatch = NO;
|
|
|
|
|
|
gid = [handler sourceGlobalID];//OK
|
|
context = [handler editingContext];//OK
|
|
|
|
entity = [_database entityNamed:[gid entityName]];//-done
|
|
relationship = [entity relationshipNamed:[handler relationshipName]];//-done
|
|
maxBatch = [relationship numberOfToManyFaultsToBatchFetch];//-done
|
|
|
|
batchBuffer = [_batchToManyFaultBuffer objectForKey:[entity name]];
|
|
bufHandler = [batchBuffer objectForKey:[relationship name]];
|
|
|
|
objects = [NSMutableArray array];
|
|
|
|
[objects addObject:[context objectForGlobalID:gid]];//-done
|
|
|
|
firstHandler = lastHandler = nil;
|
|
usedHandler = handler;
|
|
|
|
if (bufHandler && [bufHandler isEqual:usedHandler] == YES)
|
|
changeBatch = YES;
|
|
|
|
if (maxBatch > 1)
|
|
{
|
|
maxBatch--;
|
|
|
|
while (maxBatch--)
|
|
{
|
|
if (lastHandler == nil)
|
|
{
|
|
usedHandler = (EOAccessArrayFaultHandler *)[usedHandler
|
|
previous];
|
|
|
|
if (usedHandler)
|
|
firstHandler = usedHandler;
|
|
else
|
|
lastHandler = usedHandler = (EOAccessArrayFaultHandler *)
|
|
[handler next];
|
|
}
|
|
else
|
|
{
|
|
usedHandler = (EOAccessArrayFaultHandler *)[lastHandler next];
|
|
|
|
if (usedHandler)
|
|
lastHandler = usedHandler;
|
|
}
|
|
|
|
if (usedHandler == nil)
|
|
break;
|
|
|
|
if (bufHandler && [bufHandler isEqual:usedHandler] == YES)
|
|
changeBatch = YES;
|
|
|
|
[objects addObject:[context objectForGlobalID:[usedHandler
|
|
sourceGlobalID]]];
|
|
}
|
|
}
|
|
|
|
if (firstHandler == nil)
|
|
firstHandler = handler;
|
|
if (lastHandler == nil)
|
|
lastHandler = handler;
|
|
|
|
usedHandler = (id)[firstHandler previous];
|
|
bufHandler = (id)[lastHandler next];
|
|
if (usedHandler)
|
|
[usedHandler _linkNext:bufHandler];
|
|
|
|
usedHandler = bufHandler;
|
|
if (usedHandler)
|
|
{
|
|
[usedHandler _linkPrev:[firstHandler previous]];
|
|
if (bufHandler == nil)
|
|
bufHandler = usedHandler;
|
|
}
|
|
|
|
if (changeBatch == YES)
|
|
{
|
|
if (bufHandler)
|
|
[batchBuffer setObject:bufHandler
|
|
forKey:[relationship name]];
|
|
else
|
|
[batchBuffer removeObjectForKey:[relationship name]];
|
|
}
|
|
|
|
[self batchFetchRelationship:relationship
|
|
forSourceObjects:objects
|
|
editingContext:context];
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
- (void) _fireFault: (id)object
|
|
{
|
|
//TODO
|
|
BOOL fetchIt = YES;//MIRKO
|
|
|
|
|
|
|
|
//MIRKO
|
|
NSDebugMLLog(@"EODatabaseContext",@"Fire Fault: object %p of class %@",
|
|
object,[object class]);
|
|
|
|
if (_delegateRespondsTo.shouldFetchObjectFault == YES)
|
|
{
|
|
fetchIt = [_delegate databaseContext: self
|
|
shouldFetchObjectFault: object];
|
|
}
|
|
|
|
if (fetchIt)
|
|
{
|
|
EOAccessFaultHandler *handler;
|
|
EOEditingContext *context;
|
|
EOGlobalID *gid;
|
|
NSDictionary *snapshot;
|
|
EOEntity *entity = nil;
|
|
NSString *entityName = nil;
|
|
|
|
handler = (EOAccessFaultHandler *)[EOFault handlerForFault: object];
|
|
context = [handler editingContext];
|
|
gid = [handler globalID];
|
|
snapshot = EODatabaseContext_snapshotForGlobalIDWithImpPtr(self,NULL,gid); //nil
|
|
|
|
if (snapshot)
|
|
{
|
|
//TODO _fireFault snapshot
|
|
NSEmitTODO();
|
|
// [self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
entity = [self entityForGlobalID: gid];
|
|
entityName = [entity name];
|
|
|
|
if ([entity cachesObjects])
|
|
{
|
|
//TODO _fireFault [entity cachesObjects]
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
//??? generation # EOAccessGenericFaultHandler//ret 2
|
|
{
|
|
EOAccessFaultHandler *previousHandler;
|
|
EOAccessFaultHandler *nextHandler;
|
|
EOFetchSpecification *fetchSpecif;
|
|
NSArray *objects;
|
|
EOQualifier *qualifier;
|
|
/*int maxNumberOfInstancesToBatchFetch =
|
|
[entity maxNumberOfInstancesToBatchFetch];
|
|
NSDictionary *snapshot = [self snapshotForGlobalID: gid];*///nil //TODO use it !
|
|
NSDictionary *pk = [entity primaryKeyForGlobalID: (EOKeyGlobalID *)gid];
|
|
EOQualifier *pkQualifier = [entity qualifierForPrimaryKey: pk];
|
|
NSMutableArray *qualifiers = [NSMutableArray array];
|
|
|
|
[qualifiers addObject: pkQualifier];
|
|
|
|
previousHandler = (EOAccessFaultHandler *)[handler previous];
|
|
nextHandler = (EOAccessFaultHandler *)[handler next]; //nil
|
|
|
|
fetchSpecif = AUTORELEASE([EOFetchSpecification new]);
|
|
[fetchSpecif setEntityName: entityName];
|
|
|
|
qualifier = [EOOrQualifier qualifierWithQualifierArray: qualifiers];
|
|
|
|
[fetchSpecif setQualifier: qualifier];
|
|
|
|
objects = [self objectsWithFetchSpecification: fetchSpecif
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"objects %p=%@ class=%@",
|
|
objects, objects, [objects class]);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
- (void)_batchToOne:(id)fault
|
|
withHandler:(EOAccessFaultHandler *)handler
|
|
{
|
|
EOAccessFaultHandler *usedHandler, *firstHandler, *lastHandler;
|
|
EOAccessFaultHandler *bufHandler;
|
|
EOFetchSpecification *fetch;
|
|
EOEditingContext *context;
|
|
EOKeyGlobalID *gid;
|
|
EOQualifier *qualifier;
|
|
EOEntity *entity;
|
|
NSMutableArray *qualifierArray;
|
|
BOOL batch = YES, changeBatch = NO;
|
|
unsigned int maxBatch;
|
|
|
|
if (_delegateRespondsTo.shouldFetchObjectFault == YES)//-done
|
|
batch = [_delegate databaseContext:self
|
|
shouldFetchObjectFault:fault];//-done
|
|
|
|
if (batch == NO)//-done
|
|
return;//-done
|
|
|
|
gid = [handler globalID];//-done
|
|
context = [handler editingContext];//-done
|
|
|
|
entity = [_database entityNamed:[gid entityName]];//-done
|
|
maxBatch = [entity maxNumberOfInstancesToBatchFetch];//-done
|
|
|
|
bufHandler = [_batchFaultBuffer objectForKey:[entity name]];
|
|
|
|
firstHandler = lastHandler = nil;
|
|
usedHandler = handler;
|
|
|
|
if (bufHandler && [bufHandler isEqual:usedHandler] == YES)
|
|
changeBatch = YES;
|
|
|
|
if (maxBatch <= 1)
|
|
{
|
|
qualifier = [entity qualifierForPrimaryKey:
|
|
[entity primaryKeyForGlobalID:gid]];
|
|
}
|
|
else
|
|
{
|
|
qualifierArray = [NSMutableArray array];
|
|
|
|
[qualifierArray addObject:
|
|
[entity qualifierForPrimaryKey:
|
|
[entity primaryKeyForGlobalID:gid]]];
|
|
|
|
maxBatch--;
|
|
|
|
while (maxBatch--)
|
|
{
|
|
if (lastHandler == nil)
|
|
{
|
|
usedHandler = (EOAccessFaultHandler *)[usedHandler previous];
|
|
|
|
if (usedHandler)
|
|
firstHandler = usedHandler;
|
|
else
|
|
lastHandler = usedHandler = (EOAccessFaultHandler *)[handler
|
|
next];
|
|
}
|
|
else
|
|
{
|
|
usedHandler = (EOAccessFaultHandler *)[lastHandler next];
|
|
|
|
if (usedHandler)
|
|
lastHandler = usedHandler;
|
|
}
|
|
|
|
if (usedHandler == nil)
|
|
break;
|
|
|
|
if (changeBatch == NO &&
|
|
bufHandler && [bufHandler isEqual:usedHandler] == YES)
|
|
changeBatch = YES;
|
|
|
|
[qualifierArray addObject:
|
|
[entity qualifierForPrimaryKey:
|
|
[entity primaryKeyForGlobalID:
|
|
[usedHandler globalID]]]];
|
|
}
|
|
|
|
qualifier = AUTORELEASE([[EOOrQualifier alloc]
|
|
initWithQualifierArray:qualifierArray]);
|
|
}
|
|
|
|
if (firstHandler == nil)
|
|
firstHandler = handler;
|
|
if (lastHandler == nil)
|
|
lastHandler = handler;
|
|
|
|
usedHandler = (id)[firstHandler previous];
|
|
bufHandler = (id)[lastHandler next];
|
|
if (usedHandler)
|
|
[usedHandler _linkNext:bufHandler];
|
|
|
|
usedHandler = bufHandler;
|
|
if (usedHandler)
|
|
{
|
|
[usedHandler _linkPrev:[firstHandler previous]];
|
|
if (bufHandler == nil)
|
|
bufHandler = usedHandler;
|
|
}
|
|
|
|
if (changeBatch == YES)
|
|
{
|
|
if (bufHandler)
|
|
[_batchFaultBuffer setObject:bufHandler
|
|
forKey:[entity name]];
|
|
else
|
|
[_batchFaultBuffer removeObjectForKey:[entity name]];
|
|
}
|
|
|
|
fetch = [EOFetchSpecification fetchSpecificationWithEntityName:[entity name]
|
|
qualifier:qualifier
|
|
sortOrderings:nil];//-done
|
|
|
|
[context objectsWithFetchSpecification:fetch];//-done
|
|
}*/
|
|
|
|
|
|
// 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).
|
|
|
|
- (void)_addBatchForGlobalID: (EOKeyGlobalID *)globalID
|
|
fault: (EOFault *)fault
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (fault)
|
|
{
|
|
EOAccessGenericFaultHandler *handler = nil;
|
|
NSString *entityName = [globalID entityName];
|
|
|
|
|
|
|
|
handler = [_batchFaultBuffer objectForKey: entityName];
|
|
|
|
|
|
|
|
if (handler)
|
|
{
|
|
[(EOAccessGenericFaultHandler *)
|
|
[EOFault handlerForFault: fault]
|
|
linkAfter: handler
|
|
usingGeneration: [handler generation]];
|
|
}
|
|
else
|
|
{
|
|
handler = (EOAccessGenericFaultHandler *)[EOFault handlerForFault:
|
|
fault];
|
|
|
|
NSAssert1(handler, @"No handler for fault:%@", fault);
|
|
|
|
[_batchFaultBuffer setObject: handler
|
|
forKey: entityName];
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)_removeBatchForGlobalID: (EOKeyGlobalID *)globalID
|
|
fault: (EOFault *)fault
|
|
{
|
|
EOAccessGenericFaultHandler *handler, *prevHandler, *nextHandler;
|
|
NSString *entityName = [globalID entityName];
|
|
|
|
handler = (EOAccessGenericFaultHandler *)[EOFault handlerForFault: fault];
|
|
|
|
prevHandler = [handler previous];
|
|
nextHandler = [handler next];
|
|
|
|
if (prevHandler)
|
|
[prevHandler _linkNext: nextHandler];
|
|
if (nextHandler)
|
|
[nextHandler _linkPrev: prevHandler];
|
|
|
|
if ([_batchFaultBuffer objectForKey: entityName] == handler)
|
|
{
|
|
if (prevHandler)
|
|
[_batchFaultBuffer setObject: prevHandler
|
|
forKey: entityName];
|
|
else if (nextHandler)
|
|
[_batchFaultBuffer setObject: nextHandler
|
|
forKey: entityName];
|
|
else
|
|
[_batchFaultBuffer removeObjectForKey: entityName];
|
|
}
|
|
}
|
|
|
|
- (void)_addToManyBatchForSourceGlobalID: (EOKeyGlobalID *)globalID
|
|
relationshipName: (NSString *)relationshipName
|
|
fault: (EOFault *)fault
|
|
{
|
|
if (fault)
|
|
{
|
|
NSMutableDictionary *buf;
|
|
EOAccessGenericFaultHandler *handler;
|
|
NSString *entityName = [globalID entityName];
|
|
|
|
buf = [_batchToManyFaultBuffer objectForKey: entityName];
|
|
|
|
if (buf == nil)
|
|
{
|
|
buf = [NSMutableDictionary dictionaryWithCapacity: 8];
|
|
[_batchToManyFaultBuffer setObject: buf
|
|
forKey: entityName];
|
|
}
|
|
|
|
handler = [buf objectForKey: relationshipName];
|
|
|
|
if (handler)
|
|
{
|
|
[(EOAccessGenericFaultHandler *)
|
|
[EOFault handlerForFault: fault]
|
|
linkAfter: handler
|
|
usingGeneration: [handler generation]];
|
|
}
|
|
else
|
|
[buf setObject: [EOFault handlerForFault: fault]
|
|
forKey: relationshipName];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EODatabaseContext (EODatabaseSnapshotting)
|
|
|
|
- (void)recordSnapshot: (NSDictionary *)snapshot
|
|
forGlobalID: (EOGlobalID *)gid
|
|
{
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"self=%p database=%p",
|
|
self, _database);
|
|
NSDebugMLLog(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@",
|
|
self, _uniqueStack, _uniqueStack);
|
|
|
|
if ([_uniqueStack count] > 0)
|
|
{
|
|
NSMutableDictionary *snapshots = [_uniqueStack lastObject];
|
|
|
|
[snapshots setObject: snapshot
|
|
forKey: gid];
|
|
}
|
|
else
|
|
{
|
|
NSEmitTODO();
|
|
NSWarnLog(@"_uniqueStack is empty. May be there's no runing transaction !", "");
|
|
|
|
[self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@",
|
|
self, _uniqueStack, _uniqueStack);
|
|
|
|
|
|
}
|
|
|
|
- (NSDictionary *)snapshotForGlobalID: (EOGlobalID *)gid
|
|
{
|
|
return [self snapshotForGlobalID: gid
|
|
after: EODistantPastTimeInterval];
|
|
}
|
|
|
|
- (NSDictionary *)snapshotForGlobalID: (EOGlobalID *)gid
|
|
after: (NSTimeInterval)ti
|
|
{
|
|
//OK
|
|
NSDictionary *snapshot = nil;
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"self=%p database=%p",
|
|
self, _database);
|
|
|
|
|
|
snapshot = [self localSnapshotForGlobalID: gid];
|
|
|
|
if (!snapshot)
|
|
{
|
|
NSAssert(_database, @"No database");
|
|
snapshot = [_database snapshotForGlobalID: gid
|
|
after: ti];
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapshot for gid %@: %p %@",
|
|
gid, snapshot, snapshot);
|
|
|
|
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
- (void)recordSnapshot: (NSArray *)gids
|
|
forSourceGlobalID: (EOGlobalID *)gid
|
|
relationshipName: (NSString *)name
|
|
{
|
|
|
|
|
|
NSEmitTODO();
|
|
|
|
[self notImplemented: _cmd]; //TODO
|
|
/*
|
|
NSMutableDictionary *toMany = [_toManySnapshots objectForKey:gid];
|
|
|
|
if (toMany == nil)
|
|
{
|
|
toMany = [NSMutableDictionary dictionaryWithCapacity:16];
|
|
[_toManySnapshots setObject:toMany
|
|
forKey:gid];
|
|
}
|
|
|
|
[toMany setObject:gids
|
|
forKey:name];
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
- (NSArray *)snapshotForSourceGlobalID: (EOGlobalID *)gid
|
|
relationshipName: (NSString *)name
|
|
{
|
|
NSArray *snapshot = nil;
|
|
|
|
|
|
NSEmitTODO();
|
|
|
|
[self notImplemented: _cmd]; //TODO
|
|
/*
|
|
|
|
snapshot = [[_toManySnapshots objectForKey:gid] objectForKey:name];
|
|
if (!snapshot)
|
|
snapshot=[_database snapshotForSourceGlobalID:gid
|
|
relationshipName:name];
|
|
*/
|
|
|
|
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
- (NSDictionary *)localSnapshotForGlobalID: (EOGlobalID *)gid
|
|
{
|
|
//OK
|
|
NSDictionary *snapshot = nil;
|
|
int snapshotsDictCount = 0;
|
|
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"self=%p database=%p",
|
|
self, _database);
|
|
|
|
snapshotsDictCount = [_uniqueStack count];
|
|
|
|
if (snapshotsDictCount>0)
|
|
{
|
|
int i = 0;
|
|
IMP oaiIMP=[_uniqueStack methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i = 0; !snapshot && i < snapshotsDictCount; i++)
|
|
{
|
|
NSDictionary *snapshots = GDL2_ObjectAtIndexWithImp(_uniqueStack,oaiIMP,i);
|
|
snapshot = [snapshots objectForKey: gid];
|
|
}
|
|
};
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"snapshot for gid %@: %p %@",
|
|
gid, snapshot, snapshot);
|
|
|
|
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
- (NSArray *)localSnapshotForSourceGlobalID: (EOGlobalID *)gid
|
|
relationshipName: (NSString *)name
|
|
{
|
|
NSArray *snapshot = nil;
|
|
|
|
//TODO
|
|
|
|
NSEmitTODO();
|
|
|
|
[self notImplemented: _cmd]; //TODO
|
|
/*
|
|
return [[_toManySnapshots objectForKey:gid] objectForKey:name];
|
|
*/
|
|
|
|
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
- (void)forgetSnapshotForGlobalID: (EOGlobalID *)gid
|
|
{
|
|
//TODO-VERIFY deleteStack
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"self=%p database=%p [_uniqueStack count]=%d",
|
|
self, _database,[_uniqueStack count]);
|
|
|
|
if ([_uniqueStack count] > 0)
|
|
{
|
|
NSMutableDictionary *uniqueSS = [_uniqueStack lastObject];
|
|
NSMutableDictionary *uniqArSS = [_uniqueArrayStack lastObject];
|
|
NSMutableSet *deleteSS = [_deleteStack lastObject];
|
|
|
|
[deleteSS addObject: gid];
|
|
[uniqueSS removeObjectForKey: gid];
|
|
[uniqArSS removeObjectForKey: gid];
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)forgetSnapshotsForGlobalIDs: (NSArray *)gids
|
|
{
|
|
unsigned i, j, n, m;
|
|
NSMutableDictionary *snapshots;
|
|
NSMutableSet *deleteGIDs;
|
|
EOGlobalID *gid;
|
|
|
|
|
|
n = [_uniqueStack count];
|
|
if (n>0)
|
|
{
|
|
IMP oaiIMP=[_uniqueStack methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i=0; i<n; i++)
|
|
{
|
|
snapshots = GDL2_ObjectAtIndexWithImp(_uniqueStack,oaiIMP,i);
|
|
[snapshots removeObjectsForKeys: gids];
|
|
}
|
|
};
|
|
|
|
n = [_uniqueArrayStack count];
|
|
if (n>0)
|
|
{
|
|
IMP oaiIMP
|
|
= [_uniqueArrayStack methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
for (i=0; i<n; i++)
|
|
{
|
|
snapshots = GDL2_ObjectAtIndexWithImp(_uniqueArrayStack,oaiIMP,i);
|
|
[snapshots removeObjectsForKeys: gids];
|
|
}
|
|
};
|
|
|
|
n = [_deleteStack count];
|
|
if (n>0)
|
|
{
|
|
IMP oaiIMP=[_deleteStack methodForSelector: @selector(objectAtIndex:)];
|
|
IMP oaiIMP2=[gids methodForSelector: @selector(objectAtIndex:)];
|
|
|
|
m = [gids count];
|
|
for (i=0; i<n; i++)
|
|
{
|
|
deleteGIDs = GDL2_ObjectAtIndexWithImp(_deleteStack,oaiIMP,i);
|
|
for (j=0; j<m; j++)
|
|
{
|
|
gid = GDL2_ObjectAtIndexWithImp(gids, oaiIMP2, j);
|
|
[deleteGIDs removeObject: gid];
|
|
}
|
|
}
|
|
}
|
|
|
|
[_database forgetSnapshotsForGlobalIDs: gids];
|
|
|
|
}
|
|
|
|
- (void)recordSnapshots: (NSDictionary *)snapshots
|
|
{
|
|
|
|
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd]; //TODO
|
|
/*
|
|
[_snapshots addEntriesFromDictionary:snapshots];
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
- (void)recordToManySnapshots: (NSDictionary *)snapshots
|
|
{
|
|
|
|
//OK
|
|
|
|
if ([_uniqueArrayStack count] > 0)
|
|
{
|
|
NSMutableDictionary *toManySnapshots = [_uniqueArrayStack lastObject];
|
|
NSArray *keys = [snapshots allKeys];
|
|
int count = [keys count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[keys methodForSelector: @selector(objectAtIndex:)];
|
|
int i = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id key = GDL2_ObjectAtIndexWithImp(keys,oaiIMP,i);
|
|
NSDictionary *snapshotsDict = [snapshots objectForKey: key];
|
|
NSMutableDictionary *currentSnapshotsDict =
|
|
[toManySnapshots objectForKey: key];
|
|
|
|
if (!currentSnapshotsDict)
|
|
{
|
|
currentSnapshotsDict = (NSMutableDictionary *)[NSMutableDictionary
|
|
dictionary];
|
|
[toManySnapshots setObject: currentSnapshotsDict
|
|
forKey: key];
|
|
}
|
|
|
|
[currentSnapshotsDict addEntriesFromDictionary: snapshotsDict];
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)registerLockedObjectWithGlobalID: (EOGlobalID *)globalID
|
|
{
|
|
|
|
|
|
if (!_lockedObjects)
|
|
{
|
|
_lockedObjects
|
|
= NSCreateHashTable(NSNonOwnedPointerHashCallBacks, _LOCK_BUFFER);
|
|
}
|
|
|
|
NSHashInsert(_lockedObjects, globalID);
|
|
|
|
|
|
}
|
|
|
|
- (BOOL)isObjectLockedWithGlobalID: (EOGlobalID *)globalID
|
|
{
|
|
BOOL result;
|
|
|
|
|
|
|
|
result = (_lockedObjects && NSHashGet(_lockedObjects, globalID) != nil);
|
|
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)initializeObject: (id)object
|
|
row: (NSDictionary*)row
|
|
entity: (EOEntity*)entity
|
|
editingContext: (EOEditingContext*)context
|
|
{
|
|
//really near ok
|
|
NSArray *relationships = nil;
|
|
NSArray *classPropertyAttributeNames = nil;
|
|
NSUInteger count = 0;
|
|
IMP objectTakeStoredValueForKeyIMP=NULL;
|
|
IMP rowObjectForKeyIMP=NULL;
|
|
|
|
|
|
|
|
|
|
|
|
classPropertyAttributeNames = [entity classPropertyAttributeNames];
|
|
count = [classPropertyAttributeNames count];
|
|
|
|
|
|
|
|
//row is usuallly a EOMutableKnownKeyDictionary so will use EOMKKD_objectForKeyWithImpPtr
|
|
|
|
if (count>0)
|
|
{
|
|
NSUInteger i=0;
|
|
IMP oaiIMP=[classPropertyAttributeNames methodForSelector:@selector(objectAtIndex:)];
|
|
|
|
NSAssert(!_isFault(object),
|
|
@"Object is a fault. call -methodForSelector: on it is a bad idea");
|
|
|
|
objectTakeStoredValueForKeyIMP=[object methodForSelector:@selector(takeStoredValue:forKey:)];
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id key = GDL2_ObjectAtIndexWithImp(classPropertyAttributeNames,oaiIMP,i);
|
|
id value = nil;
|
|
|
|
|
|
value = EOMKKD_objectForKeyWithImpPtr(row,&rowObjectForKeyIMP,key);
|
|
|
|
if (value == GDL2_EONull)
|
|
value = nil;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"value (%p)",
|
|
value);
|
|
NSDebugMLLog(@"EODatabaseContext", @"value (%p)=%@ (class: %@)",
|
|
value, value, [value class]);
|
|
|
|
GDL2_TakeStoredValueForKeyWithImp(object,objectTakeStoredValueForKeyIMP,
|
|
value,key);
|
|
}
|
|
};
|
|
|
|
relationships = [entity _relationshipsToFaultForRow: row];
|
|
|
|
|
|
|
|
count = [relationships count];
|
|
|
|
if (count>0)
|
|
{
|
|
NSUInteger i=0;
|
|
IMP oaiIMP=[relationships methodForSelector:@selector(objectAtIndex:)];
|
|
|
|
if (!objectTakeStoredValueForKeyIMP)
|
|
{
|
|
NSAssert(!_isFault(object),
|
|
@"Object is a fault. call -methodForSelector: on it is a bad idea");
|
|
|
|
objectTakeStoredValueForKeyIMP=[object methodForSelector:@selector(takeStoredValue:forKey:)];
|
|
};
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id relObject = nil;
|
|
EORelationship *relationship = GDL2_ObjectAtIndexWithImp(relationships,oaiIMP,i);
|
|
NSString *relName = [relationship name];
|
|
|
|
|
|
if ([relationship isToMany])
|
|
{
|
|
EOGlobalID *gid = [entity globalIDForRow: row];
|
|
|
|
relObject = [self arrayFaultWithSourceGlobalID: gid
|
|
relationshipName: relName
|
|
editingContext: context];
|
|
}
|
|
else if ([relationship isFlattened])
|
|
{
|
|
// to one flattened relationship like aRelationship.anotherRelationship...
|
|
|
|
// I don't know how to handle this case.... May be we shouldn't treat this as real property ??
|
|
NSEmitTODO();
|
|
relObject = nil;
|
|
}
|
|
else
|
|
{
|
|
EOMutableKnownKeyDictionary *foreignKeyForSourceRow = nil;
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationship=%@ foreignKeyInDestination:%d",
|
|
relName,
|
|
[relationship foreignKeyInDestination]);
|
|
|
|
foreignKeyForSourceRow = [relationship _foreignKeyForSourceRow: row];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"row=%@\nforeignKeyForSourceRow:%@",
|
|
row, foreignKeyForSourceRow);
|
|
|
|
if (![foreignKeyForSourceRow
|
|
containsObjectsNotIdenticalTo: GDL2_EONull])
|
|
{
|
|
NSLog(@"foreignKeyForSourceRow=%@",[foreignKeyForSourceRow debugDescription]);
|
|
NSEmitTODO();//TODO: what to do if rel is mandatory ?
|
|
relObject = nil;
|
|
}
|
|
else
|
|
{
|
|
EOEntity *destinationEntity = [relationship destinationEntity];
|
|
EOGlobalID *relRowGid = [destinationEntity
|
|
globalIDForRow: foreignKeyForSourceRow];
|
|
|
|
|
|
|
|
if ([(EOKeyGlobalID*)relRowGid areKeysAllNulls])
|
|
NSWarnLog(@"All key of relRowGid %p (%@) are nulls",
|
|
relRowGid,
|
|
relRowGid);
|
|
|
|
relObject = [context faultForGlobalID: relRowGid
|
|
editingContext: context];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"relObject=%p (%@)",
|
|
relObject, [relObject class]);
|
|
//end
|
|
/*
|
|
NSArray *joins = [(EORelationship *)prop joins];
|
|
EOJoin *join;
|
|
NSMutableDictionary *row;
|
|
EOGlobalID *faultGID;
|
|
int h, count;
|
|
id value, realValue = nil;
|
|
|
|
row = [NSMutableDictionary dictionaryWithCapacity:4];
|
|
|
|
count = [joins count];
|
|
for (h=0; h<count; h++)
|
|
{
|
|
join = [joins objectAtIndex:h];
|
|
|
|
value = [snapshot objectForKey:[[join sourceAttribute]
|
|
name]];
|
|
if (value == null)
|
|
realValue = nil;
|
|
else
|
|
realValue = value;
|
|
|
|
[[prop validateValue:&realValue] raise];
|
|
|
|
[row setObject:value
|
|
forKey:[[join destinationAttribute]
|
|
name]];
|
|
}
|
|
|
|
if (realValue || [prop isMandatory] == YES)
|
|
{
|
|
faultGID = [[(EORelationship *)prop destinationEntity]
|
|
globalIDForRow:row];
|
|
|
|
fault = [context objectForGlobalID:faultGID];
|
|
|
|
if (fault == nil)
|
|
fault = [context faultForGlobalID:faultGID
|
|
editingContext:context];
|
|
}
|
|
else
|
|
fault = nil;
|
|
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
|
|
GDL2_TakeStoredValueForKeyWithImp(object,objectTakeStoredValueForKeyIMP,
|
|
relObject,relName);
|
|
}
|
|
};
|
|
|
|
|
|
}
|
|
|
|
- (void)forgetAllLocks
|
|
{
|
|
if (_lockedObjects)
|
|
{
|
|
NSResetHashTable(_lockedObjects);
|
|
}
|
|
}
|
|
|
|
- (void)forgetLocksForObjectsWithGlobalIDs: (NSArray *)gids
|
|
{
|
|
if (_lockedObjects)
|
|
{
|
|
unsigned n;
|
|
EOGlobalID *gid;
|
|
|
|
n = [gids count];
|
|
|
|
if (n>0)
|
|
{
|
|
IMP oaiIMP=[gids methodForSelector: @selector(objectAtIndex:)];
|
|
unsigned i = 0;
|
|
|
|
for (i=0; i<n; i++)
|
|
{
|
|
gid = GDL2_ObjectAtIndexWithImp(gids,oaiIMP,i);
|
|
NSHashRemove(_lockedObjects, gid);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
- (void)_rollbackTransaction
|
|
{
|
|
|
|
|
|
if ([_uniqueStack count] > 0)
|
|
{
|
|
[self forgetAllLocks];
|
|
|
|
[_uniqueStack removeLastObject];
|
|
[_uniqueArrayStack removeLastObject];
|
|
[_deleteStack removeLastObject];
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)_commitTransaction
|
|
{
|
|
|
|
if ([_uniqueStack count] > 0)
|
|
{
|
|
NSMutableDictionary *snapshotsDict = [_uniqueStack lastObject];
|
|
NSMutableDictionary *toManySnapshotsDict = [_uniqueArrayStack lastObject];
|
|
NSMutableSet *deleteSnapshotsSet = [_deleteStack lastObject];
|
|
NSEnumerator *deletedGIDEnum = [deleteSnapshotsSet objectEnumerator];
|
|
EOGlobalID *gid;
|
|
|
|
while ((gid = [deletedGIDEnum nextObject]))
|
|
{
|
|
[_database forgetSnapshotForGlobalID: gid];
|
|
}
|
|
|
|
[_database recordSnapshots: snapshotsDict];
|
|
[_database recordToManySnapshots: toManySnapshotsDict];
|
|
|
|
[self forgetAllLocks];
|
|
|
|
[_uniqueStack removeLastObject];
|
|
[_uniqueArrayStack removeLastObject];
|
|
[_deleteStack removeLastObject];
|
|
}
|
|
|
|
}
|
|
|
|
- (void) _beginTransaction
|
|
{
|
|
[_uniqueStack addObject: [NSMutableDictionary dictionary]];
|
|
[_uniqueArrayStack addObject: [NSMutableDictionary dictionary]];
|
|
[_deleteStack addObject: [NSMutableSet set]];
|
|
}
|
|
|
|
- (EODatabaseChannel*) _obtainOpenChannel
|
|
{
|
|
EODatabaseChannel *channel = [self availableChannel];
|
|
|
|
if (![self _openChannelWithLoginPanel: channel])
|
|
{
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd];//TODO
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
- (BOOL) _openChannelWithLoginPanel: (EODatabaseChannel*)databaseChannel
|
|
{
|
|
// veridy: LoginPanel ???
|
|
EOAdaptorChannel *adaptorChannel = [databaseChannel adaptorChannel];
|
|
|
|
if (![adaptorChannel isOpen]) //??
|
|
{
|
|
[adaptorChannel openChannel];
|
|
}
|
|
|
|
return [adaptorChannel isOpen];
|
|
}
|
|
|
|
- (void) _forceDisconnect
|
|
{ // TODO
|
|
NSEmitTODO();
|
|
[self notImplemented: _cmd];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EODatabaseContext(EOMultiThreaded)
|
|
|
|
- (void)lock
|
|
{
|
|
[_lock lock];
|
|
}
|
|
|
|
- (void)unlock
|
|
{
|
|
[_lock unlock];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation EODatabaseContext (EODatabaseContextPrivate2)
|
|
|
|
- (void) _verifyNoChangesToReadonlyEntity: (EODatabaseOperation*)dbOpe
|
|
{
|
|
//TODO
|
|
EOEntity *entity = nil;
|
|
|
|
|
|
|
|
entity = [dbOpe entity];
|
|
|
|
|
|
|
|
if ([entity isReadOnly])
|
|
{
|
|
//?? exception I presume
|
|
}
|
|
else
|
|
{
|
|
[dbOpe databaseOperator]; //SoWhat
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void) _cleanUpAfterSave
|
|
{
|
|
// TODO -- dw
|
|
//EODatabase * eodatabase = [self database];
|
|
|
|
_coordinator = nil; //realesae ?
|
|
_editingContext = nil; //realesae ?
|
|
|
|
if (_dbOperationsByGlobalID)
|
|
{
|
|
//Really free it because we don't want to record some db ope (select for exemple).
|
|
NSFreeMapTable(_dbOperationsByGlobalID);
|
|
_dbOperationsByGlobalID = NULL;
|
|
}
|
|
|
|
_flags.beganTransaction = NO;
|
|
_flags.willPrepareForSave = NO;
|
|
_flags.preparingForSave = NO;
|
|
|
|
// TODO -- dw
|
|
// if (eodatabase != nil)
|
|
// {
|
|
// [eodatabase _clearLastRecords];
|
|
// }
|
|
|
|
}
|
|
|
|
- (EOGlobalID*)_globalIDForObject: (id)object
|
|
{
|
|
EOEditingContext *objectEditingContext = nil;
|
|
EOGlobalID *gid = nil;
|
|
|
|
|
|
NSAssert(object, @"No object");
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",@"object=%p of class %@",
|
|
object,[object class]);
|
|
NSDebugMLLog(@"EODatabaseContext", @"_editingContext=%p",
|
|
_editingContext);
|
|
|
|
objectEditingContext = [object editingContext];
|
|
NSAssert2(objectEditingContext, @"No editing context for object %p: %@",
|
|
object,object);
|
|
|
|
gid=EOEditingContext_globalIDForObjectWithImpPtr(objectEditingContext,
|
|
NULL,
|
|
object);
|
|
|
|
|
|
if (!gid)
|
|
{
|
|
NSEmitTODO();
|
|
NSLog(@"TODO: no GID in EODatabaseContext _globalIDForObject:");
|
|
//TODO exception ? ==> RollbackCh
|
|
}
|
|
|
|
|
|
|
|
return gid;
|
|
}
|
|
|
|
- (NSDictionary*)_primaryKeyForObject: (id)object
|
|
{
|
|
//Ayers: Review
|
|
return [self _primaryKeyForObject: object
|
|
raiseException: YES];
|
|
}
|
|
|
|
- (NSDictionary*)_primaryKeyForObject: (id)object
|
|
raiseException: (BOOL)raiseException
|
|
{
|
|
//NEAR OK
|
|
//Ayers: Review
|
|
NSDictionary *pk = nil;
|
|
EOEntity *entity = nil;
|
|
NSArray *pkNames = nil;
|
|
BOOL shouldGeneratePrimaryKey = NO;
|
|
|
|
|
|
|
|
|
|
NSAssert(!_isNilOrEONull(object), @"No object");
|
|
|
|
entity = [_database entityForObject: object];
|
|
shouldGeneratePrimaryKey = [self _shouldGeneratePrimaryKeyForEntityName:
|
|
[entity name]];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p shouldGeneratePrimaryKey=%d",
|
|
object, shouldGeneratePrimaryKey);
|
|
|
|
/*
|
|
if (shouldGeneratePrimaryKey)
|
|
*/
|
|
{
|
|
|
|
BOOL isPKValid = NO;
|
|
EOGlobalID *gid = EODatabaseContext_globalIDForObjectWithImpPtr(self,NULL,object);
|
|
|
|
|
|
|
|
pk = [entity primaryKeyForGlobalID: (EOKeyGlobalID*)gid]; //OK
|
|
|
|
|
|
{
|
|
NSDictionary *pk2 = nil;
|
|
pkNames = [entity primaryKeyAttributeNames];
|
|
|
|
pk2 = [self valuesForKeys: pkNames
|
|
object: object];
|
|
|
|
|
|
|
|
if (pk)
|
|
{
|
|
//merge pk2 into pk
|
|
NSEnumerator *pk2Enum = [pk2 keyEnumerator];
|
|
IMP pk2EnumNO=NULL; // nextObject
|
|
NSMutableDictionary *realPK
|
|
= [NSMutableDictionary dictionaryWithDictionary: pk];//revoir
|
|
id key = nil;
|
|
|
|
while ((key = GDL2_NextObjectWithImpPtr(pk2Enum,&pk2EnumNO)))
|
|
{
|
|
[realPK setObject: [pk2 objectForKey: key]
|
|
forKey: key];
|
|
}
|
|
|
|
pk = realPK;
|
|
}
|
|
else
|
|
pk=pk2;
|
|
}
|
|
|
|
|
|
|
|
isPKValid = [entity isPrimaryKeyValidInObject: pk];
|
|
NSDebugMLLog(@"EODatabaseContext",@"object=%p isPKValid=%d",
|
|
object, isPKValid);
|
|
if (isPKValid == NO)
|
|
pk = nil;
|
|
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p isPKValid=%d shouldGeneratePrimaryKey=%d",
|
|
object, isPKValid, shouldGeneratePrimaryKey);
|
|
|
|
if (isPKValid == NO && shouldGeneratePrimaryKey)
|
|
{
|
|
pk = nil;
|
|
|
|
if (_delegateRespondsTo.newPrimaryKey == YES)
|
|
pk = [_delegate databaseContext: self
|
|
newPrimaryKeyForObject: object
|
|
entity: entity];
|
|
|
|
if (!pk)
|
|
{
|
|
NSArray *pkAttributes = nil;
|
|
EOAdaptorChannel *channel = nil;
|
|
EOStoredProcedure *nextPKProcedure = nil;
|
|
|
|
nextPKProcedure = [entity storedProcedureForOperation:
|
|
EONextPrimaryKeyProcedureOperation];
|
|
#if 0 // TODO execute storedProcedure and fetch results;
|
|
|
|
if (nextPKProcedure)
|
|
[[[self _obtainOpenChannel] adaptorChannel]
|
|
executeStoredProcedure: nextPKProcedure
|
|
withValues:];
|
|
#endif
|
|
|
|
pkAttributes = [entity primaryKeyAttributes];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"object=%p pk=%@ [pkAttributes count]=%d",
|
|
object, pk, [pkAttributes count]);
|
|
|
|
if (pk == nil && [pkAttributes count] == 1)
|
|
{
|
|
EOAttribute *pkAttr = [pkAttributes objectAtIndex: 0];
|
|
//TODO attr: adaptorValueType //returned EOAdaptorNumberType
|
|
//TODO [entity rootParent];//so what
|
|
|
|
if (channel == nil)
|
|
{
|
|
channel = [[self _obtainOpenChannel] adaptorChannel];
|
|
|
|
if ([[channel adaptorContext]
|
|
transactionNestingLevel] == 0)
|
|
[[channel adaptorContext] beginTransaction];
|
|
|
|
if (_flags.beganTransaction == NO)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"BEGAN TRANSACTION FLAG==>NO");
|
|
_flags.beganTransaction = YES;
|
|
}
|
|
}
|
|
|
|
pk = [channel primaryKeyForNewRowWithEntity: entity];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"** prepare pk %@", pk);
|
|
|
|
|
|
if (pk == nil && [[pkAttr valueClassName]
|
|
isEqual:@"NSData"] == YES)
|
|
{
|
|
unsigned char data[EOUniqueBinaryKeyLength];
|
|
|
|
[EOTemporaryGlobalID assignGloballyUniqueBytes: data];
|
|
|
|
pk = [NSDictionary dictionaryWithObject:
|
|
[NSData dataWithBytes: data
|
|
length:
|
|
EOUniqueBinaryKeyLength]
|
|
forKey: [pkAttr name]];
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!pk && raiseException)
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ -- %@ 0x%x: cannot generate primary key for object '%@'",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self, object];
|
|
}
|
|
}
|
|
/*
|
|
else // !shouldGeneratePrimaryKey
|
|
*/
|
|
if (!pk)
|
|
{
|
|
// MG
|
|
// Here we'll find if there is a "parent" objects which relay pk
|
|
// I'm not sure if we could do it here but it's handle this case:
|
|
// object is a new child of a previous existing object.
|
|
// if we don't generate it's pk here, the chld object
|
|
// will relay it's pk to parent which is not good
|
|
NSDictionary *objectSnapshot=nil;
|
|
NSArray *relationships=nil;
|
|
NSUInteger relationshipsCount=0;
|
|
|
|
|
|
|
|
// get object snapshot
|
|
objectSnapshot = [object snapshot];
|
|
NSDebugMLLog(@"EODatabaseContext", @"objectSnapshot=%@",
|
|
objectSnapshot);
|
|
|
|
// Get object relationships
|
|
relationships = [entity relationships];
|
|
|
|
|
|
relationshipsCount = [relationships count];
|
|
|
|
if (relationshipsCount>0)
|
|
{
|
|
IMP oaiIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
NSUInteger i = 0;
|
|
|
|
for (i = 0; i < relationshipsCount; i++)
|
|
{
|
|
EORelationship *inverseRelationship = nil;
|
|
|
|
EORelationship *relationship = GDL2_ObjectAtIndexWithImp(relationships,oaiIMP,i);
|
|
NSDebugMLLog(@"EODatabaseContext", @"relationship=%@",
|
|
relationship);
|
|
|
|
inverseRelationship = [relationship inverseRelationship];
|
|
NSDebugMLLog(@"EODatabaseContext", @"inverseRelationship=%@",
|
|
inverseRelationship);
|
|
|
|
// if there's inverse relationship with propagates primary key
|
|
if ([inverseRelationship propagatesPrimaryKey])
|
|
{
|
|
NSString *relationshipName= [relationship name];
|
|
NSDictionary *relationshipValuePK = nil;
|
|
|
|
// get object value for the relationship
|
|
id relationshipValue
|
|
= [objectSnapshot valueForKey:relationshipName];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"entity name=%@ relationship name=%@ Value=%@",
|
|
[entity name],
|
|
relationshipName, relationshipValue);
|
|
|
|
// get relationshipValue pk
|
|
NSAssert2(!_isNilOrEONull(relationshipValue),
|
|
@"No relationshipValue for relationship %@ in objectSnapshot %@ ",
|
|
relationshipName,
|
|
objectSnapshot);
|
|
|
|
relationshipValuePK
|
|
= [self _primaryKeyForObject: relationshipValue];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"relationshipValuePK=%@",
|
|
relationshipValuePK);
|
|
|
|
// force object to relay pk now !
|
|
[self relayPrimaryKey: relationshipValuePK
|
|
object: relationshipValue
|
|
entity: [_database entityForObject: relationshipValue]];
|
|
};
|
|
};
|
|
};
|
|
pk = [self valuesForKeys: pkNames
|
|
object: object];
|
|
|
|
if (![entity isPrimaryKeyValidInObject: pk])
|
|
pk=nil;
|
|
};
|
|
|
|
if (pk)
|
|
{
|
|
EODatabaseOperation *dbOpe = [self databaseOperationForObject: object];
|
|
NSMutableDictionary *newRow = [dbOpe newRow];
|
|
|
|
[newRow addEntriesFromDictionary: pk];// VERIFY Here we replace
|
|
// previous key
|
|
}
|
|
|
|
|
|
NSDebugMLog(@"object %p=%@\npk=%@",object, object, pk);
|
|
|
|
|
|
|
|
return pk;
|
|
}
|
|
|
|
- (BOOL) _shouldGeneratePrimaryKeyForEntityName: (NSString*)entityName
|
|
{
|
|
//OK
|
|
BOOL shouldGeneratePK = YES;
|
|
|
|
|
|
|
|
if (_nonPrimaryKeyGenerators)
|
|
shouldGeneratePK = !NSHashGet(_nonPrimaryKeyGenerators, entityName);
|
|
|
|
NSDebugMLLog(@"EODatabaseContext", @"shouldGeneratePK for %@: %s",
|
|
entityName,
|
|
(shouldGeneratePK ? "YES" : "NO"));
|
|
NSAssert(![entityName isEqualToString: @"Country"]
|
|
|| shouldGeneratePK, @"MGVALID: Failed");
|
|
|
|
|
|
|
|
return shouldGeneratePK;
|
|
}
|
|
|
|
- (void)_buildPrimaryKeyGeneratorListForEditingContext: (EOEditingContext*)context
|
|
{
|
|
NSArray *objects[3];
|
|
NSHashTable *processedEntities = NULL;
|
|
NSMutableArray *entityToProcess = nil;
|
|
NSUInteger which;
|
|
|
|
|
|
|
|
if (_nonPrimaryKeyGenerators)
|
|
{
|
|
NSResetHashTable(_nonPrimaryKeyGenerators);
|
|
}
|
|
|
|
processedEntities = NSCreateHashTable(NSObjectHashCallBacks, 32);
|
|
|
|
objects[0] = [context updatedObjects];
|
|
objects[1] = [context insertedObjects];
|
|
objects[2] = [context deletedObjects];
|
|
|
|
for (which = 0; which < 3; which++)
|
|
{
|
|
NSUInteger count = [objects[which] count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP oaiIMP=[objects[which] methodForSelector: @selector(objectAtIndex:)];
|
|
NSUInteger i = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
id object = GDL2_ObjectAtIndexWithImp(objects[which],oaiIMP,i);
|
|
EOEntity *entity = [_database entityForObject: object];
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"add entity to process: %@", [entity name]);
|
|
|
|
if (entityToProcess)
|
|
[entityToProcess addObject: entity];
|
|
else
|
|
entityToProcess = [NSMutableArray arrayWithObject: entity];
|
|
}
|
|
};
|
|
}
|
|
|
|
while ([entityToProcess count])
|
|
{
|
|
EOEntity *entity = [entityToProcess lastObject];
|
|
|
|
|
|
|
|
[entityToProcess removeLastObject];
|
|
|
|
if (!NSHashInsertIfAbsent(processedEntities, entity)) //Already processed ?
|
|
{
|
|
NSArray *relationships = [entity relationships];
|
|
NSUInteger relationshipsCount = [relationships count];
|
|
|
|
if (relationshipsCount>0)
|
|
{
|
|
IMP relObjectAtIndexIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
NSUInteger iRelationship = 0;
|
|
|
|
for (iRelationship = 0;
|
|
iRelationship < relationshipsCount;
|
|
iRelationship++)
|
|
{
|
|
EORelationship *relationship = GDL2_ObjectAtIndexWithImp(relationships,relObjectAtIndexIMP,iRelationship);
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"test entity: %@ relationship=%@",
|
|
[entity name],
|
|
relationship);
|
|
|
|
if ([relationship propagatesPrimaryKey])
|
|
{
|
|
EOEntity *destinationEntity = [relationship
|
|
destinationEntity];
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"test entity: %@ destinationEntity=%@",
|
|
[entity name],
|
|
[destinationEntity name]);
|
|
|
|
if (destinationEntity)
|
|
{
|
|
NSArray *destAttrs;
|
|
NSArray *pkAttrs;
|
|
NSUInteger count;
|
|
BOOL destPK = NO;
|
|
|
|
destAttrs = [relationship destinationAttributes];
|
|
pkAttrs = [destinationEntity primaryKeyAttributes];
|
|
count = [destAttrs count];
|
|
|
|
if (count>0)
|
|
{
|
|
IMP destAttrsObjectAtIndexIMP=[relationships methodForSelector: @selector(objectAtIndex:)];
|
|
NSUInteger i=0;
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if ([pkAttrs containsObject:
|
|
GDL2_ObjectAtIndexWithImp(destAttrs,destAttrsObjectAtIndexIMP,i)])
|
|
destPK = YES;
|
|
}
|
|
};
|
|
if (destPK)
|
|
{
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"destination entity: %@ "
|
|
@"No PK generation [Rel = %@]",
|
|
[destinationEntity name],
|
|
[relationship name]);
|
|
|
|
if (!_nonPrimaryKeyGenerators)
|
|
_nonPrimaryKeyGenerators = NSCreateHashTable(NSObjectHashCallBacks, 32);
|
|
|
|
NSHashInsertIfAbsent(_nonPrimaryKeyGenerators, [destinationEntity name]);
|
|
[entityToProcess addObject: destinationEntity];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"_nonPrimaryKeyGenerators=%@",
|
|
NSStringFromHashTable(_nonPrimaryKeyGenerators));
|
|
|
|
|
|
|
|
NSFreeHashTable(processedEntities);
|
|
}
|
|
|
|
/** Returns a dictionary containing a snapshot of object that reflects its committed values (last values putted in the database; i.e. values before changes were made on the object).
|
|
It is updated after commiting new values.
|
|
If the object has been just inserted, the dictionary is empty.
|
|
**/
|
|
- (NSDictionary*)_currentCommittedSnapshotForObject: (id)object
|
|
{
|
|
NSDictionary *snapshot = nil;
|
|
EOGlobalID *gid = nil;
|
|
EODatabaseOperation *dbOpe = nil;
|
|
EODatabaseOperator dbOperator = (EODatabaseOperator)0;
|
|
|
|
|
|
|
|
gid = EOEditingContext_globalIDForObjectWithImpPtr(_editingContext,NULL,object);
|
|
dbOpe = [self databaseOperationForGlobalID: gid]; //I'm not sure. Retrieve it directly ?
|
|
dbOperator = [dbOpe databaseOperator];
|
|
|
|
switch (dbOperator)
|
|
{
|
|
case EODatabaseUpdateOperator:
|
|
snapshot = [_editingContext committedSnapshotForObject: object];//OK
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"snapshot %p=%@",
|
|
snapshot, snapshot);
|
|
break;
|
|
|
|
case EODatabaseInsertOperator:
|
|
snapshot = [NSDictionary dictionary];
|
|
break;
|
|
|
|
//TODO
|
|
/* else
|
|
snapshot=XX;//TODO
|
|
*/
|
|
case EODatabaseDeleteOperator:
|
|
break;
|
|
|
|
case EODatabaseNothingOperator:
|
|
break;
|
|
}
|
|
|
|
NSDebugMLLog(@"EODatabaseContext",
|
|
@"snapshot %p=%@",
|
|
snapshot, snapshot);
|
|
|
|
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
- (void) _assertValidStateWithSelector: (SEL)sel
|
|
{
|
|
if ((!_flags.preparingForSave) && (!_flags.willPrepareForSave))
|
|
{
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"_assertValidStateWithSelector:%s %s is in invalid state, "
|
|
"call prepareForSaveWithCoordinator: before calling this method.",
|
|
sel_getName(sel),
|
|
object_getClassName(self)];
|
|
|
|
}
|
|
}
|
|
|
|
- (id) _addDatabaseContextStateToException: (id)param0
|
|
{
|
|
NSEmitTODO();
|
|
return [self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
- (id) _databaseContextState
|
|
{
|
|
NSEmitTODO();
|
|
return [self notImplemented: _cmd]; //TODO
|
|
}
|
|
|
|
@end
|