From 021d9c67592ee678a6a35f44e8f035a4c2258e4b Mon Sep 17 00:00:00 2001 From: Dave Wetzel Date: Sun, 30 May 2010 18:24:14 +0000 Subject: [PATCH] * EOAccess/EOAttribute.m added attributeWithParent: definition: convenience method * EOAccess/EOAdaptorContext.m _channelDidInit: setDelegate * EOAccess/EODatabaseContext.m availableChannel: call registerChannel new method _fetchRawRowKeyPaths:fetchSpecification:entity:editingContext: * EOAccess/EODatabaseChannel.m raise NSInvalidArgumentException in init. initWithDatabaseContext: more checks * EOAdaptors/PostgreSQLAdaptor/PostgreSQLContext.m beginTransaction: more checks, raise NSInternalInconsistencyException git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@30494 72102866-910b-0410-8b05-ffd578937521 --- EOAccess/EOAdaptorContext.m | 21 +- EOAccess/EOAttribute.h | 5 + EOAccess/EOAttribute.m | 14 + EOAccess/EODatabaseChannel.m | 26 +- EOAccess/EODatabaseContext.m | 524 +++++++++++------- .../PostgreSQLAdaptor/PostgreSQLContext.m | 16 +- 6 files changed, 389 insertions(+), 217 deletions(-) diff --git a/EOAccess/EOAdaptorContext.m b/EOAccess/EOAdaptorContext.m index 2bc25ea..16d2b1e 100644 --- a/EOAccess/EOAdaptorContext.m +++ b/EOAccess/EOAdaptorContext.m @@ -113,11 +113,11 @@ NSString *EOAdaptorContextRollbackTransactionNotification = @"EOAdaptorContextRo - (BOOL)hasOpenChannels { int i, count = [_channels count]; - + for (i = 0; i < count; i++) if ([[[_channels objectAtIndex: i] nonretainedObjectValue] isOpen]) return YES; - + return NO; } @@ -277,23 +277,22 @@ NSString *EOAdaptorContextRollbackTransactionNotification = @"EOAdaptorContextRo { [_channels addObject: [NSValue valueWithNonretainedObject: channel]]; - [channel setDebugEnabled: [self isDebugEnabled]]; -//call self delegate -//call channel setDelegate: returned ? + [channel setDebugEnabled: [self isDebugEnabled]]; // not done on reference -- dw + [channel setDelegate:_delegate]; } - (void)_channelWillDealloc:channel { int i; - + for (i = [_channels count] - 1; i >= 0; i--) + { + if ([[_channels objectAtIndex: i] nonretainedObjectValue] == channel) { - if ([[_channels objectAtIndex: i] nonretainedObjectValue] == channel) - { - [_channels removeObjectAtIndex: i]; - break; - } + [_channels removeObjectAtIndex: i]; + break; } + } } @end diff --git a/EOAccess/EOAttribute.h b/EOAccess/EOAttribute.h index c7f4c41..b6a0822 100644 --- a/EOAccess/EOAttribute.h +++ b/EOAccess/EOAttribute.h @@ -116,6 +116,11 @@ typedef enum { + (id)attributeWithPropertyList: (NSDictionary *)propertyList owner: (id)owner; +/** returns an autoreleased attribute **/ + ++ (id) attributeWithParent:(EOEntity *) parent + definition:(NSString*) def; + /* Accessing the entity */ - (NSString *)name; diff --git a/EOAccess/EOAttribute.m b/EOAccess/EOAttribute.m index 9d9b30f..943d0d0 100644 --- a/EOAccess/EOAttribute.m +++ b/EOAccess/EOAttribute.m @@ -101,6 +101,20 @@ RCS_ID("$Id$") owner: owner] autorelease]; } ++ (id) attributeWithParent:(EOEntity *) parent + definition:(NSString*) def +{ + EOAttribute * attr = [[[self alloc] init] autorelease]; + + if (attr) { + [attr setName: def]; + [attr setParent: parent]; + [attr setDefinition: def]; + } + + return attr; +} + - (id) initWithPropertyList: (NSDictionary *)propertyList owner: (id)owner { diff --git a/EOAccess/EODatabaseChannel.m b/EOAccess/EODatabaseChannel.m index 867b161..0a4d879 100644 --- a/EOAccess/EODatabaseChannel.m +++ b/EOAccess/EODatabaseChannel.m @@ -112,20 +112,30 @@ RCS_ID("$Id$") return [[[self alloc] initWithDatabaseContext: databaseContext] autorelease]; } +- (id) init +{ + [NSException raise: NSInvalidArgumentException + format: @"Use initWithDatabaseContext to init an instance of class %@", + NSStringFromClass([self class])]; + + return nil; +} + - (id) initWithDatabaseContext:(EODatabaseContext *)databaseContext { if ((self = [super init])) { - ASSIGN(_databaseContext, databaseContext); - ASSIGN(_adaptorChannel, [[_databaseContext adaptorContext] + ASSIGN(_adaptorChannel, [[databaseContext adaptorContext] createAdaptorChannel]); -//TODO NO<<<< - [_adaptorChannel openChannel]; - _fetchProperties = [NSMutableArray new]; - _fetchSpecifications = [NSMutableArray new]; -//NO>>>>>>> - [_databaseContext registerChannel: self];//should be in caller + if (!_adaptorChannel) + { + [NSException raise: NSInternalInconsistencyException + format: @"EODatabaseChannel is unable to obtain new channel from %@", + [databaseContext adaptorContext]]; + } else { + ASSIGN(_databaseContext, databaseContext); + } } return self; diff --git a/EOAccess/EODatabaseContext.m b/EOAccess/EODatabaseContext.m index 54898a1..2e1513a 100644 --- a/EOAccess/EODatabaseContext.m +++ b/EOAccess/EODatabaseContext.m @@ -537,8 +537,14 @@ static Class _contextClass = Nil; } } - if (!channel) + if ((!channel) && ([_registeredChannels count] < 1)) { channel = [EODatabaseChannel databaseChannelWithDatabaseContext: self]; + if (channel) + { + [self registerChannel:channel]; + } + + } return channel; } @@ -1260,8 +1266,8 @@ userInfo = { } - (void)_fetchRelationship: (EORelationship *)relationship - withObjects: (NSArray *)objsArray - editingContext: (EOEditingContext *)context + withObjects: (NSArray *)objsArray + editingContext: (EOEditingContext *)context { NSMutableArray *qualArray = nil; NSEnumerator *objEnum = nil; @@ -1322,8 +1328,154 @@ userInfo = { EOFLOGObjectFnStop(); } +- (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 + editingContext: (EOEditingContext *)context { // TODO EODatabaseChannel *channel = nil; NSMutableArray *array = nil; @@ -1348,6 +1500,7 @@ userInfo = { NSDebugMLLog(@"EODatabaseContext", @"fetchSpecification=%@", fetchSpecification); #warning fix this method! -- dw + channel = [self _obtainOpenChannel]; if (_flags.beganTransaction == NO) { @@ -1399,192 +1552,187 @@ userInfo = { */ rawRowKeyPaths = [fetchSpecification rawRowKeyPaths];//OK if (rawRowKeyPaths) -#if 0 - { - NSEmitTODO(); - [self notImplemented: _cmd]; //TODO - } -#else - // (stephane@sente.ch) Adapted implementation of non raw rows { - //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 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]; - - NSDebugMLLog(@"EODatabaseContext", @"Will Fetch"); - - 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 - { - NSDebugMLLog(@"EODatabaseContext", @"AFTER FETCH"); - 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]); - NSDebugMLLog(@"EODatabaseContext", @"array=%@", array); - - //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]); - } -#endif - - else if ([entity cachesObjects] == YES)//OK + 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]; + + NSDebugMLLog(@"EODatabaseContext", @"Will Fetch"); + + 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 + { + NSDebugMLLog(@"EODatabaseContext", @"AFTER FETCH"); + 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]); + NSDebugMLLog(@"EODatabaseContext", @"array=%@", array); + + //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; diff --git a/EOAdaptors/PostgreSQLAdaptor/PostgreSQLContext.m b/EOAdaptors/PostgreSQLAdaptor/PostgreSQLContext.m index 741f18b..ce2cfa6 100644 --- a/EOAdaptors/PostgreSQLAdaptor/PostgreSQLContext.m +++ b/EOAdaptors/PostgreSQLAdaptor/PostgreSQLContext.m @@ -82,8 +82,6 @@ RCS_ID("$Id$") { PostgreSQLChannel *channel = nil; - EOFLOGObjectFnStart(); - if ([self transactionNestingLevel]) [NSException raise: NSInternalInconsistencyException format: @"%@ -- %@ 0x%x: attempted to begin a transaction within a transaction", @@ -101,8 +99,13 @@ RCS_ID("$Id$") self]; } + if ((!_channels) || ([_channels count] < 1)) { + [NSException raise:NSInternalInconsistencyException + format:@"%s: No open channel found. CreateAdaptorChannel first!", + __PRETTY_FUNCTION__]; + } channel = [[_channels objectAtIndex: 0] nonretainedObjectValue]; - + if ([channel isOpen] == NO) [NSException raise: PostgreSQLException format: @"cannot execute SQL expression. Channel is not opened."]; @@ -117,13 +120,6 @@ RCS_ID("$Id$") if (_delegateRespondsTo.didBegin) [_delegate adaptorContextDidBegin: self]; - - NSDebugMLLog(@"gsdb", @"_flags.didBegin=%s", - (_flags.didBegin ? "YES" : "NO")); - NSDebugMLLog(@"gsdb", @"_flags.didAutoBegin=%s", - (_flags.didAutoBegin ? "YES" : "NO")); - - EOFLOGObjectFnStop(); } - (void)commitTransaction