From 57830dbb8874a544c0447479b29eab6cc52a35aa Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 17 Mar 2009 15:58:25 +0000 Subject: [PATCH] Monotone-Parent: 89b88978ce7a84af4dd6e2e230ede1c3ef700068 Monotone-Revision: c29fcc5ce939d41d4b8a3795a566530292ec2f0f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-03-17T15:58:25 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/ChangeLog | 8 + SOPE/GDLContentStore/GCSFolder.m | 355 ++++++++++++++++--------------- 2 files changed, 191 insertions(+), 172 deletions(-) diff --git a/SOPE/GDLContentStore/ChangeLog b/SOPE/GDLContentStore/ChangeLog index 19b1315ae..79a66f65b 100644 --- a/SOPE/GDLContentStore/ChangeLog +++ b/SOPE/GDLContentStore/ChangeLog @@ -1,3 +1,11 @@ +2009-03-17 Wolfgang Sourdeau + + * GCSFolder.m ([GCSFolder + -writeContent:_contenttoName:_namebaseVersion:_baseVersion]): + reorganized method to centralize error management, enabling us to + remove the "CHECKERROR" macro. The method was also not closing + channels whenever an error occured. + 2008-09-22 Wolfgang Sourdeau * GCSFolder.m ([GCSFolder diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index 14873185f..641060b0c 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -50,15 +50,6 @@ typedef enum { bothTableRequired = 3 } GCSTableRequirement; -#define CHECKERROR() \ - if (error) { \ - [[storeChannel adaptorContext] rollbackTransaction]; \ - [[quickChannel adaptorContext] rollbackTransaction]; \ - [self logWithFormat:@"ERROR(%s): cannot %s content : %@", \ - __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update", error]; \ - return error; \ - } \ - @implementation GCSFolder static BOOL debugOn = NO; @@ -815,182 +806,202 @@ static NSArray *contentFieldNames = nil; NSMutableDictionary *quickRow, *contentRow; NSDictionary *currentRow; GCSFieldExtractor *extractor; - NSException *error; NSNumber *storedVersion; BOOL isNewRecord, hasInsertDelegate, hasUpdateDelegate; NSCalendarDate *nowDate; NSNumber *now; EOEntity *quickTableEntity, *storeTableEntity; NSArray *rows; + NSException *error; /* check preconditions */ - if (_name == nil) { - return [NSException exceptionWithName:@"GCSStoreException" - reason:@"no content filename was provided" - userInfo:nil]; - } - if (_content == nil) { - return [NSException exceptionWithName:@"GCSStoreException" - reason:@"no content was provided" - userInfo:nil]; - } - - /* run */ - error = nil; - nowDate = [NSCalendarDate date]; - now = [NSNumber numberWithUnsignedInt:[nowDate timeIntervalSince1970]]; - - if (doLogStore) - [self logWithFormat:@"should store content: '%@'\n%@", _name, _content]; - - rows = [self fetchFields: [NSArray arrayWithObjects: - @"c_version", - @"c_deleted", - nil] - fetchSpecification: [self _simpleFetchSpecificationWith: - @"c_name" - andValue: _name] - ignoreDeleted: NO]; - if ([rows count]) + if (_name) { - currentRow = [rows objectAtIndex: 0]; - storedVersion = [currentRow objectForKey: @"c_version"]; - if (doLogStore) - [self logWithFormat:@" version: %@", storedVersion]; - if ([[currentRow objectForKey: @"c_deleted"] intValue] > 0) + if (_content) { - [self _purgeRecordWithName: _name]; - isNewRecord = YES; - } + /* run */ + error = nil; + nowDate = [NSCalendarDate date]; + now = [NSNumber numberWithUnsignedInt:[nowDate timeIntervalSince1970]]; + + if (doLogStore) + [self logWithFormat:@"should store content: '%@'\n%@", _name, _content]; + + rows = [self fetchFields: [NSArray arrayWithObjects: + @"c_version", + @"c_deleted", + nil] + fetchSpecification: [self _simpleFetchSpecificationWith: + @"c_name" + andValue: _name] + ignoreDeleted: NO]; + if ([rows count]) + { + currentRow = [rows objectAtIndex: 0]; + storedVersion = [currentRow objectForKey: @"c_version"]; + if (doLogStore) + [self logWithFormat:@" version: %@", storedVersion]; + if ([[currentRow objectForKey: @"c_deleted"] intValue] > 0) + { + [self _purgeRecordWithName: _name]; + isNewRecord = YES; + } + else + isNewRecord = NO; + } + else + { + storedVersion = nil; + isNewRecord = YES; + } + + /* check whether sequence matches */ + /* use version = 0 to override check */ + if (_baseVersion == 0 + || _baseVersion == [storedVersion unsignedIntValue]) + { + /* extract quick info */ + extractor = [folderInfo quickExtractor]; + quickRow = [extractor extractQuickFieldsFromContent:_content]; + if (quickRow) + { + [quickRow setObject:_name forKey:@"c_name"]; + + if (doLogStore) + [self logWithFormat:@" store quick: %@", quickRow]; + + /* make content row */ + contentRow = [NSMutableDictionary dictionaryWithCapacity:16]; + + if (ofFlags.sameTableForQuick) + [contentRow addEntriesFromDictionary:quickRow]; + + [contentRow setObject:_name forKey:@"c_name"]; + [contentRow setObject:now forKey:@"c_lastmodified"]; + if (isNewRecord) + { + [contentRow setObject:now forKey:@"c_creationdate"]; + [contentRow setObject:[NSNumber numberWithInt:0] + forKey:@"c_version"]; + } + else // TODO: increase version? + [contentRow setObject: + [NSNumber numberWithInt:([storedVersion intValue] + 1)] + forKey:@"c_version"]; + [contentRow setObject:_content forKey:@"c_content"]; + + /* open channels */ + storeChannel = [self acquireStoreChannel]; + if (storeChannel) + { + if (!ofFlags.sameTableForQuick) + { + quickChannel = [self acquireQuickChannel]; + if (!quickChannel) + { + [self errorWithFormat:@"%s: could not open quick channel!", + __PRETTY_FUNCTION__]; + [self releaseChannel:storeChannel]; + return nil; + } + } + + /* we check if we can call directly methods on our adaptor + channel delegate. If not, we generate SQL ourself since it'll + be a little bit faster and less complex than using GDL to do so */ + hasInsertDelegate = [[storeChannel delegate] + respondsToSelector: @selector(adaptorChannel:willInsertRow:forEntity:)]; + hasUpdateDelegate = [[storeChannel delegate] + respondsToSelector: @selector(adaptorChannel:willUpdateRow:describedByQualifier:)]; + + [[quickChannel adaptorContext] beginTransaction]; + [[storeChannel adaptorContext] beginTransaction]; + + quickTableEntity = [self _quickTableEntity]; + storeTableEntity = [self _storeTableEntity]; + + if (isNewRecord) + { + if (!ofFlags.sameTableForQuick) + error = (hasInsertDelegate + ? [quickChannel insertRowX: quickRow forEntity: quickTableEntity] + : [quickChannel + evaluateExpressionX: [self _generateInsertStatementForRow: quickRow + tableName: [self quickTableName]]]); + + if (!error) + error = (hasInsertDelegate + ? [storeChannel insertRowX: contentRow forEntity: storeTableEntity] + : [storeChannel + evaluateExpressionX: [self _generateInsertStatementForRow: contentRow + tableName: [self storeTableName]]]); + } + else + { + if (!ofFlags.sameTableForQuick) + error = (hasUpdateDelegate + ? [quickChannel updateRowX: quickRow + describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" + isEqualTo: _name andColumn: nil isEqualTo: nil + entity: quickTableEntity]] + : [quickChannel evaluateExpressionX: [self _generateUpdateStatementForRow: quickRow + tableName: [self quickTableName] + whereColumn: @"c_name" isEqualTo: _name + andColumn: nil isEqualTo: nil]]); + if (!error) + error = (hasUpdateDelegate + ? [storeChannel updateRowX: contentRow + describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" isEqualTo: _name + andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) + isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt:_baseVersion] : (NSNumber *)nil) + entity: storeTableEntity]] + : [storeChannel evaluateExpressionX: [self _generateUpdateStatementForRow: contentRow tableName:[self storeTableName] + whereColumn: @"c_name" isEqualTo: _name + andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) + isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt: _baseVersion] : (NSNumber *)nil)]]); + } + + if (error) + { + [[storeChannel adaptorContext] rollbackTransaction]; + [[quickChannel adaptorContext] rollbackTransaction]; + [self logWithFormat: + @"ERROR(%s): cannot %s content : %@", __PRETTY_FUNCTION__, + isNewRecord ? "insert" : "update", + error]; + } + else + { + [[storeChannel adaptorContext] commitTransaction]; + [[quickChannel adaptorContext] commitTransaction]; + } + + [self releaseChannel: storeChannel]; + if (!ofFlags.sameTableForQuick) + [self releaseChannel: quickChannel]; + } + else + [self errorWithFormat:@"%s: could not open storage channel!", + __PRETTY_FUNCTION__]; + } + else + error = [self errorExtractorReturnedNoQuickRow:extractor + forContent:_content]; + } + else /* version mismatch (concurrent update) */ + error = [self errorVersionMismatchBetweenStoredVersion: + [storedVersion unsignedIntValue] + andExpectedVersion: _baseVersion]; + } else - isNewRecord = NO; + error = [NSException exceptionWithName:@"GCSStoreException" + reason:@"no content was provided" + userInfo:nil]; } else - { - storedVersion = nil; - isNewRecord = YES; - } - - /* check whether sequence matches */ - if (_baseVersion != 0 /* use 0 to override check */) { - if (_baseVersion != [storedVersion unsignedIntValue]) { - /* version mismatch (concurrent update) */ - return [self errorVersionMismatchBetweenStoredVersion: - [storedVersion unsignedIntValue] - andExpectedVersion:_baseVersion]; - } - } - - /* extract quick info */ - extractor = [folderInfo quickExtractor]; - if ((quickRow = [extractor extractQuickFieldsFromContent:_content]) == nil) { - return [self errorExtractorReturnedNoQuickRow:extractor - forContent:_content]; - } - - [quickRow setObject:_name forKey:@"c_name"]; - - if (doLogStore) - [self logWithFormat:@" store quick: %@", quickRow]; - - /* make content row */ - contentRow = [NSMutableDictionary dictionaryWithCapacity:16]; - - if (ofFlags.sameTableForQuick) - [contentRow addEntriesFromDictionary:quickRow]; - - [contentRow setObject:_name forKey:@"c_name"]; - if (isNewRecord) [contentRow setObject:now forKey:@"c_creationdate"]; - [contentRow setObject:now forKey:@"c_lastmodified"]; - if (isNewRecord) - [contentRow setObject:[NSNumber numberWithInt:0] forKey:@"c_version"]; - else { - // TODO: increase version? - [contentRow setObject: - [NSNumber numberWithInt:([storedVersion intValue] + 1)] - forKey:@"c_version"]; - } - [contentRow setObject:_content forKey:@"c_content"]; - - /* open channels */ - if ((storeChannel = [self acquireStoreChannel]) == nil) { - [self errorWithFormat:@"%s: could not open storage channel!", - __PRETTY_FUNCTION__]; - return nil; - } - if (!ofFlags.sameTableForQuick) { - if ((quickChannel = [self acquireQuickChannel]) == nil) { - [self errorWithFormat:@"%s: could not open quick channel!", - __PRETTY_FUNCTION__]; - [self releaseChannel:storeChannel]; - return nil; - } - } - - /* we check if we can call directly methods on our adaptor - channel delegate. If not, we generate SQL ourself since it'll - be a little bit faster and less complex than using GDL to do so */ - hasInsertDelegate = [[storeChannel delegate] - respondsToSelector: @selector(adaptorChannel:willInsertRow:forEntity:)]; - hasUpdateDelegate = [[storeChannel delegate] - respondsToSelector: @selector(adaptorChannel:willUpdateRow:describedByQualifier:)]; - - [[quickChannel adaptorContext] beginTransaction]; - [[storeChannel adaptorContext] beginTransaction]; - - quickTableEntity = [self _quickTableEntity]; - storeTableEntity = [self _storeTableEntity]; - - if (isNewRecord) { - if (!ofFlags.sameTableForQuick) { - error = (hasInsertDelegate - ? [quickChannel insertRowX: quickRow forEntity: quickTableEntity] - : [quickChannel - evaluateExpressionX: [self _generateInsertStatementForRow: quickRow - tableName: [self quickTableName]]]); - CHECKERROR(); - } - - error = (hasInsertDelegate - ? [storeChannel insertRowX: contentRow forEntity: storeTableEntity] - : [storeChannel - evaluateExpressionX: [self _generateInsertStatementForRow: contentRow - tableName: [self storeTableName]]]); - - CHECKERROR(); - } - else { - if (!ofFlags.sameTableForQuick) { - error = (hasUpdateDelegate - ? [quickChannel updateRowX: quickRow - describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" - isEqualTo: _name andColumn: nil isEqualTo: nil - entity: quickTableEntity]] - : [quickChannel evaluateExpressionX: [self _generateUpdateStatementForRow: quickRow - tableName: [self quickTableName] - whereColumn: @"c_name" isEqualTo: _name - andColumn: nil isEqualTo: nil]]); - CHECKERROR(); - } - - error = (hasUpdateDelegate - ? [storeChannel updateRowX: contentRow - describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" isEqualTo: _name - andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) - isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt:_baseVersion] : (NSNumber *)nil) - entity: storeTableEntity]] - : [storeChannel evaluateExpressionX: [self _generateUpdateStatementForRow: contentRow tableName:[self storeTableName] - whereColumn: @"c_name" isEqualTo: _name - andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) - isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt: _baseVersion] : (NSNumber *)nil)]]); - CHECKERROR(); - } - - [[storeChannel adaptorContext] commitTransaction]; - [[quickChannel adaptorContext] commitTransaction]; - - [self releaseChannel: storeChannel]; - if (!ofFlags.sameTableForQuick) [self releaseChannel: quickChannel]; + error = [NSException exceptionWithName:@"GCSStoreException" + reason:@"no content filename was provided" + userInfo:nil]; return error; }