Tuukka Norri avatar Tuukka Norri committed aadd541

Entity observing
- Added functionality to allow receiving notifications for entities for which fetches haven't been executed. This can be done by calling -[BXDatabaseContext observeEntity:options:error:]. The notification will be kBXEntityChangeNotificationin case the user didn't request object IDs.
- Added a check for the case in which mUndoGroupingLevels hasn't been set when working with the undo manager.

Comments (0)

Files changed (14)

Sources/BXConstants.h

 BX_EXPORT NSString* const kBXDatabaseContextKey;
 BX_EXPORT NSString* const kBXURIKey;
 BX_EXPORT NSString* const kBXObjectIDsKey;
+BX_EXPORT NSString* const kBXEntityChangeNotification;
 BX_EXPORT NSString* const kBXInsertNotification;
 BX_EXPORT NSString* const kBXDeleteNotification;
 BX_EXPORT NSString* const kBXUpdateNotification;
 	kBXDatabaseObjectForeignKey
 };
 
+enum BXObservingOption
+{
+	kBXObservingOptionNone = 0,
+	kBXObservingOptionNotificationOnly,
+	kBXObservingOptionObjectIDs
+};
+
 /** \brief Property kind. */
 enum BXPropertyKind
 {

Sources/BXConstants.m

 NSString* const kBXPredicateKey = @"kBXPredicateKey";
 NSString* const kBXOwnerObjectVariableName = @"BXOwnerObject";
 
+NSString* const kBXEntityChangeNotification = @"kBXEntityChangeNotification";
 NSString* const kBXInsertNotification = @"kBXInsertNotification";
 NSString* const kBXInsertEarlyNotification = @"kBXInsertEarlyNotification";
 NSString* const kBXUpdateNotification = @"kBXUpdateNotification";

Sources/BXDatabaseContext.h

 
 - (BOOL) fireFault: (BXDatabaseObject *) anObject key: (id) aKey error: (NSError **) error;
 
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error;
+
 /* These methods should only be used for purposes which the ones above are not suited. */
 - (NSArray *) executeQuery: (NSString *) queryString error: (NSError **) error;
 - (NSArray *) executeQuery: (NSString *) queryString parameters: (NSArray *) parameters error: (NSError **) error;

Sources/BXDatabaseContext.m

 	NSUInteger groupingLevel = [mUndoManager groupingLevel];
 	NSUInteger currentLevel = NSNotFound;
 	BOOL shouldEstablishSavepoint = NO;
-	while (NSNotFound != (currentLevel = [mUndoGroupingLevels lastIndex]))
+	if (mUndoGroupingLevels)
 	{
-		if (currentLevel < groupingLevel)
-			break;
-		
-		shouldEstablishSavepoint = YES;
-		[mUndoGroupingLevels removeIndex: currentLevel];
+		while (NSNotFound != (currentLevel = [mUndoGroupingLevels lastIndex]))
+		{
+			if (currentLevel < groupingLevel)
+				break;
+			
+			shouldEstablishSavepoint = YES;
+			[mUndoGroupingLevels removeIndex: currentLevel];
+		}
 	}
 	
 	if (shouldEstablishSavepoint)
     return retval;
 }
 
+
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error
+{
+	NSError *localError = nil;
+	BOOL retval = NO;
+	if ([self checkErrorHandling])
+	{
+		if ([entity hasCapability: kBXEntityCapabilityAutomaticUpdate])
+			retval = [mDatabaseInterface observeEntity: entity options: options error: &localError];
+		else
+		{
+			// FIXME: set an error if no observing capability.
+		}
+		
+		BXHandleError (error, localError);
+	}
+	else
+	{
+		retval = YES;
+	}
+	return retval;
+}
+
+
 /** \name Deleting database objects */
 //@{
 /**
     }
 }
 
+
+- (void) changedEntity: (BXEntityDescription *) entity
+{
+	NSNotificationCenter *nc = [self notificationCenter];
+	NSDictionary *userInfo = [NSDictionary dictionaryWithObject: self forKey: kBXDatabaseContextKey];
+	[nc postNotificationName: kBXEntityChangeNotification object: entity userInfo: userInfo];
+}
+
+
 - (BOOL) handleInvalidTrust: (SecTrustRef) trust result: (SecTrustResultType) result
 {
 	BOOL retval = NO;

Sources/BXDatabaseObjectID.m

 		{
 			NSString* entityName = nil;
 			NSString* schemaName = nil;
-			BXAssertValueReturn ([[self class] parseURI: anURI entity: &entityName schema: &schemaName primaryKeyFields: NULL],
-								   nil, @"Expected object URI to be parseable.");
+			BOOL status = [[self class] parseURI: anURI entity: &entityName schema: &schemaName primaryKeyFields: NULL];
+			BXAssertValueReturn (status, nil, @"Expected object URI to be parseable.");
 			BXAssertValueReturn ([[anEntity name] isEqualToString: entityName], nil, @"Expected entity names to match.");
 			BXAssertValueReturn ([[anEntity schemaName] isEqualToString: schemaName], nil, @"Expected schema names to match.");
 		}

Sources/BXInterface.h

 @interface BXDatabaseContext (DBInterfaces)
 - (BOOL) connectedToDatabase: (BOOL) connected async: (BOOL) async error: (NSError **) error;
 - (void) connectionLost: (NSError *) error;
+- (void) changedEntity: (BXEntityDescription *) entity;
 - (void) addedObjectsToDatabase: (NSArray *) objectIDs;
 - (void) updatedObjectsInDatabase: (NSArray *) objectIDs attributes: (NSArray *) changedAttributes faultObjects: (BOOL) shouldFault;
 - (void) deletedObjectsFromDatabase: (NSArray *) objectIDs;
 - (void) reloadDatabaseMetadata;
 - (void) prepareForEntityValidation;
 - (BOOL) validateEntities: (NSArray *) entities error: (NSError **) outError;
+- (BOOL) observeEntity: (BXEntityDescription *) entity 
+			   options: (enum BXObservingOption) options 
+				 error: (NSError **) error;
 @end

Sources/BXPGAutocommitTransactionHandler.m

 
 
 @implementation BXPGAutocommitTransactionHandler (Observing)
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity error: (NSError **) error
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error
 {
-	return [self observeIfNeeded: entity connection: mConnection error: error];
+	return [self observeEntity: entity connection: mConnection options: options error: error];
 }
 
 - (void) checkSuperEntities: (BXEntityDescription *) entity

Sources/BXPGInterface.m

 }
 
 
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error
+{
+	// FIXME: set an error if transaction handler isn't set.
+	return [mTransactionHandler observeEntity: entity options: options error: error];
+}
+
+
 - (id) createObjectForEntity: (BXEntityDescription *) entity 
              withFieldValues: (NSDictionary *) valueDict
                        class: (Class) aClass 
 		goto error;
 	}
 	if ([entity hasCapability: kBXEntityCapabilityAutomaticUpdate])
-		if (! [mTransactionHandler observeIfNeeded: entity error: error]) goto error;
+		if (! [mTransactionHandler observeEntity: entity options: kBXObservingOptionObjectIDs error: error]) goto error;
 	
 	[self setCurrentlyChangedEntity: entity];
 	
 	PGTSTableDescription* table = [self tableForEntity: entity];
 	Expect (table);
 	if ([entity hasCapability: kBXEntityCapabilityAutomaticUpdate])
-		if (! [mTransactionHandler observeIfNeeded: entity error: error]) goto error;
+		if (! [mTransactionHandler observeEntity: entity options: kBXObservingOptionObjectIDs error: error]) goto error;
 	
 	PGTSConnection* connection = [mTransactionHandler connection];
 	[mQueryBuilder reset];
 	PGTSTableDescription* table = [self tableForEntity: entity];
 	Expect (table);
 	if ([entity hasCapability: kBXEntityCapabilityAutomaticUpdate])
-		if (! [mTransactionHandler observeIfNeeded: entity error: error]) goto error;
+		if (! [mTransactionHandler observeEntity: entity options: kBXObservingOptionObjectIDs error: error]) goto error;
 
 	[self setCurrentlyChangedEntity: entity];
 	

Sources/BXPGManualCommitTransactionHandler.m

 
 
 @implementation BXPGManualCommitTransactionHandler (Observing)
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity error: (NSError **) error
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error
 {
-	return [self observeIfNeeded: entity connection: mNotifyConnection error: error];
+	return [self observeEntity: entity connection: mNotifyConnection options: options error: error];
 }
 
 - (void) checkSuperEntities: (BXEntityDescription *) entity

Sources/BXPGModificationHandler.h

 {
 	NSString* mQueryString;
 	NSInteger mIdentifier;
+	enum BXObservingOption mOptions;
 }
 - (void) checkModifications: (int) backendPID;
 - (void) setIdentifier: (NSInteger) identifier;
+- (void) setObservingOptions: (enum BXObservingOption) options;
+- (void) setQueryString: (NSString *) queryString;
 @end

Sources/BXPGModificationHandler.mm

 	[super dealloc];
 }
 
+
+- (void) setObservingOptions: (enum BXObservingOption) options
+{
+	if (mOptions < options)
+	{
+		mOptions = options;
+		[self prepare];
+	}
+}
+
+
 - (void) prepare
 {
 	[super prepare];
 	
-	BXPGTableDescription* rel = [mInterface tableForEntity: mEntity];
-	PGTSIndexDescription* pkey = [rel primaryKey];
-	NSArray* columns = [[[pkey columns] allObjects] sortedArrayUsingSelector: @selector (indexCompare:)];
-	NSString* pkeyString = [(id) [[columns BX_Collect] columnDefinition] componentsJoinedByString: @", "];
+	NSString *queryString = nil;
+	switch (mOptions) 
+	{
+		case kBXObservingOptionObjectIDs:
+		{
+			BXPGTableDescription* rel = [mInterface tableForEntity: mEntity];
+			PGTSIndexDescription* pkey = [rel primaryKey];
+			NSArray* columns = [[[pkey columns] allObjects] sortedArrayUsingSelector: @selector (indexCompare:)];
+			NSString* pkeyString = [(id) [[columns BX_Collect] columnDefinition] componentsJoinedByString: @", "];
+			
+			NSString* queryFormat = 
+			@"SELECT * FROM \"baseten\".modification ($1, $2, $3, $4) "
+			@"AS m ( "
+			@"\"baseten_modification_type\" CHARACTER (1), "
+			@"\"baseten_modification_cols\" INT2 [], "
+			@"\"baseten_modification_timestamp\" TIMESTAMP (6) WITHOUT TIME ZONE, "
+			@"\"baseten_modification_insert_timestamp\" TIMESTAMP (6) WITHOUT TIME ZONE, "
+			@"%@)";
+			queryString = [NSString stringWithFormat: queryFormat, pkeyString];
+			
+			break;
+		}
+			
+		case kBXObservingOptionNotificationOnly:
+		case kBXObservingOptionNone:
+		default:
+			break;
+	}
 	
-	NSString* queryFormat = 
-	@"SELECT * FROM \"baseten\".modification ($1, $2, $3, $4) "
-	@"AS m ( "
-	@"\"baseten_modification_type\" CHARACTER (1), "
-	@"\"baseten_modification_cols\" INT2 [], "
-	@"\"baseten_modification_timestamp\" TIMESTAMP (6) WITHOUT TIME ZONE, "
-	@"\"baseten_modification_insert_timestamp\" TIMESTAMP (6) WITHOUT TIME ZONE, "
-	@"%@)";
-    mQueryString = [[NSString alloc] initWithFormat: queryFormat, pkeyString];
+	[self setQueryString: queryString];
 }
 
+
+- (void) setQueryString: (NSString *) queryString
+{
+	if (mQueryString != queryString)
+	{
+		[mQueryString release];
+		mQueryString = [queryString copy];
+	}
+}
+
+
 - (void) handleNotification: (PGTSNotification *) notification
 {
 	int backendPID = 0;
 	if (![mEntity getsChangedByTriggers] && [mInterface currentlyChangedEntity] == mEntity)
 		backendPID = [mConnection backendPID];
 
-	[self checkModifications: backendPID];
+	if ([notification backendPID] != backendPID)
+		[self checkModifications: backendPID];
 }
 
+
 - (void) checkModifications: (int) backendPID
 {
-    //When observing self-generated modifications, also the ones that still have NULL values for 
-    //pgts_modification_timestamp should be included in the query.	
-	BOOL isIdle = (PQTRANS_IDLE == [mConnection transactionStatus]);
-	
-	PGTSResultSet* res = [mConnection executeQuery: mQueryString parameters: 
-						  [NSNumber numberWithInteger: mIdentifier], [NSNumber numberWithBool: isIdle], 
-						  mLastCheck, [NSNumber numberWithInt: backendPID]];
-	BXPGTableDescription *rel = [mInterface tableForEntity: mEntity];
-	NSDictionary *attributesByName = [mEntity attributesByName];
-	BXAssertVoidReturn ([res querySucceeded], @"Expected query to succeed: %@", [res error]);
-	
-	//Update the timestamp.
-	while ([res advanceRow]) 
-		[self setLastCheck: [res valueForKey: @"baseten_modification_timestamp"]];
-	
-	//Sort the changes by type.
-	NSMutableDictionary *changes = [NSMutableDictionary dictionaryWithCapacity: 4];
-	NSMutableArray *changedAttrs = [NSMutableArray arrayWithCapacity: [res count]];
-	[res goBeforeFirstRow];
-    while ([res advanceRow])
-    {
-		unichar modificationType = [[res valueForKey: @"baseten_modification_type"] characterAtIndex: 0];                            
-		NSMutableArray *objectIDs = FindObject (changes, modificationType);
-		if (! objectIDs)
+	switch (mOptions) 
+	{
+		case kBXObservingOptionObjectIDs:
 		{
-			objectIDs = [NSMutableArray arrayWithCapacity: [res count]];
-			Insert (changes, modificationType, objectIDs);
-		}
-		
-		BXDatabaseObjectID* objectID = [BXDatabaseObjectID IDWithEntity: mEntity primaryKeyFields: [res currentRowAsDictionary]];
-		[objectIDs addObject: objectID];
-		
-		if ('U' == modificationType)
-		{
-			NSArray *columnIndices = [res valueForKey: @"baseten_modification_cols"];
-			NSMutableArray *attrs = [NSMutableArray arrayWithCapacity: [columnIndices count]];
-			BXEnumerate (currentIndex, e, [columnIndices objectEnumerator])
+			//When observing self-generated modifications, also the ones that still have NULL values for 
+			//pgts_modification_timestamp should be included in the query.	
+			BOOL isIdle = (PQTRANS_IDLE == [mConnection transactionStatus]);
+			
+			PGTSResultSet* res = [mConnection executeQuery: mQueryString parameters: 
+								  [NSNumber numberWithInteger: mIdentifier], [NSNumber numberWithBool: isIdle], 
+								  mLastCheck, [NSNumber numberWithInt: backendPID]];
+			BXPGTableDescription *rel = [mInterface tableForEntity: mEntity];
+			NSDictionary *attributesByName = [mEntity attributesByName];
+			BXAssertVoidReturn ([res querySucceeded], @"Expected query to succeed: %@", [res error]);
+			
+			//Update the timestamp.
+			while ([res advanceRow]) 
+				[self setLastCheck: [res valueForKey: @"baseten_modification_timestamp"]];
+			
+			//Sort the changes by type.
+			NSMutableDictionary *changes = [NSMutableDictionary dictionaryWithCapacity: 4];
+			NSMutableArray *changedAttrs = [NSMutableArray arrayWithCapacity: [res count]];
+			[res goBeforeFirstRow];
+			while ([res advanceRow])
 			{
-				PGTSColumnDescription *col = [rel columnAtIndex: [currentIndex integerValue]];
-				BXAttributeDescription *attr = [attributesByName objectForKey: [col name]];
-				[attrs addObject: attr];
+				unichar modificationType = [[res valueForKey: @"baseten_modification_type"] characterAtIndex: 0];                            
+				NSMutableArray *objectIDs = FindObject (changes, modificationType);
+				if (! objectIDs)
+				{
+					objectIDs = [NSMutableArray arrayWithCapacity: [res count]];
+					Insert (changes, modificationType, objectIDs);
+				}
+				
+				BXDatabaseObjectID* objectID = [BXDatabaseObjectID IDWithEntity: mEntity primaryKeyFields: [res currentRowAsDictionary]];
+				[objectIDs addObject: objectID];
+				
+				if ('U' == modificationType)
+				{
+					NSArray *columnIndices = [res valueForKey: @"baseten_modification_cols"];
+					NSMutableArray *attrs = [NSMutableArray arrayWithCapacity: [columnIndices count]];
+					BXEnumerate (currentIndex, e, [columnIndices objectEnumerator])
+					{
+						PGTSColumnDescription *col = [rel columnAtIndex: [currentIndex integerValue]];
+						BXAttributeDescription *attr = [attributesByName objectForKey: [col name]];
+						[attrs addObject: attr];
+					}
+					
+					[changedAttrs addObject: attrs];
+				}
 			}
 			
-			[changedAttrs addObject: attrs];
+			//Send changes.
+			BaseTen::ValueGetter <unichar> getter;
+			for (NSValue *key in [changes keyEnumerator])
+			{
+				NSArray* objectIDs = [changes objectForKey: key];
+				
+				unichar changeType = '\0';
+				BOOL status = getter (key, &changeType);
+				ExpectV (status);
+				
+				switch (changeType)
+				{
+					case 'I':
+						[[mInterface databaseContext] addedObjectsToDatabase: objectIDs];
+						break;
+						
+					case 'U':
+						[[mInterface databaseContext] updatedObjectsInDatabase: objectIDs attributes: changedAttrs faultObjects: YES];
+						break;
+						
+					case 'D':
+						[[mInterface databaseContext] deletedObjectsFromDatabase: objectIDs];
+						break;
+						
+					default:
+						break;
+				}
+			}
+			
+			// Fall through.
 		}
+
+		case kBXObservingOptionNotificationOnly:
+		{
+			[[mInterface databaseContext] changedEntity: mEntity];
+			break;
+		}
+			
+		default:
+			break;
 	}
-	
-	//Send changes.
-	BaseTen::ValueGetter <unichar> getter;
-	for (NSValue *key in [changes keyEnumerator])
-    {
-		NSArray* objectIDs = [changes objectForKey: key];
-
-		unichar changeType = '\0';
-		BOOL status = getter (key, &changeType);
-		ExpectV (status);
-		
-		switch (changeType)
-		{
-			case 'I':
-				[[mInterface databaseContext] addedObjectsToDatabase: objectIDs];
-				break;
-								
-			case 'U':
-				[[mInterface databaseContext] updatedObjectsInDatabase: objectIDs attributes: changedAttrs faultObjects: YES];
-				break;
-				
-			case 'D':
-				[[mInterface databaseContext] deletedObjectsFromDatabase: objectIDs];
-				break;
-				
-			default:
-				break;
-		}
-    }	
 }
 
 - (void) setIdentifier: (NSInteger) identifier

Sources/BXPGTransactionHandler.h

 	BXPGCertificateVerificationDelegate* mCertificateVerificationDelegate;
 	PGTSConnection* mConnection;
 	
-	NSMutableSet* mObservedEntities;
-	NSMutableDictionary* mObservers;
+	NSMutableDictionary *mModificationHandlersByEntity;
+	NSMutableDictionary* mEntityObserversByNotificationName;
 	NSMutableDictionary* mChangeHandlers;
 	NSMutableDictionary* mLockHandlers;
 	NSMutableDictionary* mDatabaseIdentifiers;
 - (void) handleConnectionErrorFor: (PGTSConnection *) failedConnection;
 - (void) handleSuccess;
 
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity error: (NSError **) error;
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity connection: (PGTSConnection *) connection error: (NSError **) error;
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error;
+- (BOOL) observeEntity: (BXEntityDescription *) entity 
+			connection: (PGTSConnection *) connection 
+			   options: (enum BXObservingOption) options 
+				 error: (NSError **) error;
 - (BOOL) addClearLocksHandler: (PGTSConnection *) connection error: (NSError **) outError;
 
 - (void) checkSuperEntities: (BXEntityDescription *) entity;

Sources/BXPGTransactionHandler.m

 {
 	[self disconnect];
 	[mCertificateVerificationDelegate release];
-	[mObservedEntities release];
-	[mObservers release];
+	[mModificationHandlersByEntity release];
+	[mEntityObserversByNotificationName release];
 	[mChangeHandlers release];
 	[mLockHandlers release];
 	[mDatabaseIdentifiers release];
 
 - (void) didDisconnect
 {
-	[mObservedEntities removeAllObjects];
-	[mObservers removeAllObjects];
+	[mModificationHandlersByEntity removeAllObjects];
+	[mEntityObserversByNotificationName removeAllObjects];
 	[mChangeHandlers removeAllObjects];
 	[mLockHandlers removeAllObjects];
 }
 #pragma mark Observing
 
 
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity error: (NSError **) error
+- (BOOL) observeEntity: (BXEntityDescription *) entity options: (enum BXObservingOption) options error: (NSError **) error
 {
 	[self doesNotRecognizeSelector: _cmd];
 	return NO;
 }
 
 
-- (BOOL) observeIfNeeded: (BXEntityDescription *) entity connection: (PGTSConnection *) connection error: (NSError **) error
+- (BOOL) observeEntity: (BXEntityDescription *) entity 
+			connection: (PGTSConnection *) connection 
+			   options: (enum BXObservingOption) options 
+				 error: (NSError **) error
 {
 	ExpectR (error, NO);
 	ExpectR (entity, NO);
 	
 	BOOL retval = NO;
 	
-	if ([mObservedEntities containsObject: entity])
+	BXPGModificationHandler* handler = [mModificationHandlersByEntity objectForKey: entity];
+	if (handler)
+	{
 		retval = YES;
+		[handler setObservingOptions: options];
+	}
 	else
 	{
-		if (! mObservedEntities)
-			mObservedEntities = [[NSMutableSet alloc] init];
-		if (! mObservers)
-			mObservers = [[NSMutableDictionary alloc] init];
+		if (! mModificationHandlersByEntity)
+			mModificationHandlersByEntity = [[NSMutableDictionary alloc] init];
+		if (! mEntityObserversByNotificationName)
+			mEntityObserversByNotificationName = [[NSMutableDictionary alloc] init];
 		if (! mChangeHandlers)
 			mChangeHandlers = [[NSMutableDictionary alloc] init];
 		if (! mLockHandlers)
 					[res advanceRow];
 					NSString* notificationName = [res valueForKey: @"n_name"];
 					NSNumber* dbIdentifier = [res valueForKey: @"relid"];
-					BXPGModificationHandler* handler = [[[BXPGModificationHandler alloc] init] autorelease];
+					handler = [[[BXPGModificationHandler alloc] init] autorelease];
 					
 					[handler setInterface: mInterface];
 					[handler setConnection: connection];
 					[handler setEntity: entity];
 					[handler setNotificationName: notificationName];
 					[handler setIdentifier: [[res valueForKey: @"relid"] integerValue]];
-					[handler prepare];
+					[handler setObservingOptions: options]; // Calls -prepare.
 					
-					[mObservers setObject: handler forKey: notificationName];
+					[mEntityObserversByNotificationName setObject: handler forKey: notificationName];
 					[mChangeHandlers setObject: handler forKey: entity];
 					[mDatabaseIdentifiers setObject: dbIdentifier forKey: entity];
+					[mModificationHandlersByEntity setObject: handler forKey: entity];
 				}
 				
 				{
 					[handler setLockTableName: tableName];
 					[handler prepare];
 					
-					[mObservers setObject: handler forKey: notificationName];
+					[mEntityObserversByNotificationName setObject: handler forKey: notificationName];
 					[mLockHandlers setObject: handler forKey: entity];
 				}
-								
-				[mObservedEntities addObject: entity];			
+				
 				retval = YES;
 			}
 			else
 	{
 		if (! retval)
 			break;
-		retval = [self observeIfNeeded: currentEntity connection: connection error: error];
+		retval = [self observeEntity: currentEntity connection: connection options: options error: error];
 	}
 	
     return retval;
 	
 	BOOL retval = NO;
 	NSString* nname = [BXPGClearLocksHandler notificationName];
-	if ([mObservers objectForKey: nname])
+	if ([mEntityObserversByNotificationName objectForKey: nname])
 		retval = YES;
 	else
 	{
 			[handler setInterface: mInterface];
 			[handler setConnection: connection];
 			[handler prepare];
-			[mObservers setObject: handler forKey: nname];
+			[mEntityObserversByNotificationName setObject: handler forKey: nname];
 			
 			retval = YES;
 		}
 
 - (NSArray *) observedOids
 {
-	NSMutableArray* retval = [NSMutableArray arrayWithCapacity: [mObservedEntities count]];
-	BXEnumerate (currentEntity, e, [mObservedEntities objectEnumerator])
+	NSMutableArray* retval = [NSMutableArray arrayWithCapacity: [mModificationHandlersByEntity count]];
+	BXEnumerate (currentEntity, e, [mModificationHandlersByEntity keyEnumerator])
 	{
 		NSString* name = [currentEntity name];
 		NSString* schemaName = [currentEntity schemaName];
 {
 	NSString* notificationName = [notification notificationName];
 	BXLogDebug (@"Got notification (from %p): %@", connection, notificationName);
-	[[mObservers objectForKey: notificationName] handleNotification: notification];
+	[[mEntityObserversByNotificationName objectForKey: notificationName] handleNotification: notification];
 }
 
 

Sources/PGTSResultSet.mm

 			[retval addObject: [self currentRowAsDictionary]];
 		}
 	}
-	return retval;
+	return [[retval copy] autorelease];
 }
 
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.