Monotone-Parent: 89b88978ce7a84af4dd6e2e230ede1c3ef700068
Monotone-Revision: c29fcc5ce939d41d4b8a3795a566530292ec2f0f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-03-17T15:58:25 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
b65ae175c3
commit
57830dbb88
|
@ -1,3 +1,11 @@
|
||||||
|
2009-03-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
|
* 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 <wsourdeau@inverse.ca>
|
2008-09-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
* GCSFolder.m ([GCSFolder
|
* GCSFolder.m ([GCSFolder
|
||||||
|
|
|
@ -50,15 +50,6 @@ typedef enum {
|
||||||
bothTableRequired = 3
|
bothTableRequired = 3
|
||||||
} GCSTableRequirement;
|
} 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
|
@implementation GCSFolder
|
||||||
|
|
||||||
static BOOL debugOn = NO;
|
static BOOL debugOn = NO;
|
||||||
|
@ -815,182 +806,202 @@ static NSArray *contentFieldNames = nil;
|
||||||
NSMutableDictionary *quickRow, *contentRow;
|
NSMutableDictionary *quickRow, *contentRow;
|
||||||
NSDictionary *currentRow;
|
NSDictionary *currentRow;
|
||||||
GCSFieldExtractor *extractor;
|
GCSFieldExtractor *extractor;
|
||||||
NSException *error;
|
|
||||||
NSNumber *storedVersion;
|
NSNumber *storedVersion;
|
||||||
BOOL isNewRecord, hasInsertDelegate, hasUpdateDelegate;
|
BOOL isNewRecord, hasInsertDelegate, hasUpdateDelegate;
|
||||||
NSCalendarDate *nowDate;
|
NSCalendarDate *nowDate;
|
||||||
NSNumber *now;
|
NSNumber *now;
|
||||||
EOEntity *quickTableEntity, *storeTableEntity;
|
EOEntity *quickTableEntity, *storeTableEntity;
|
||||||
NSArray *rows;
|
NSArray *rows;
|
||||||
|
NSException *error;
|
||||||
|
|
||||||
/* check preconditions */
|
/* check preconditions */
|
||||||
if (_name == nil) {
|
if (_name)
|
||||||
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])
|
|
||||||
{
|
{
|
||||||
currentRow = [rows objectAtIndex: 0];
|
if (_content)
|
||||||
storedVersion = [currentRow objectForKey: @"c_version"];
|
|
||||||
if (doLogStore)
|
|
||||||
[self logWithFormat:@" version: %@", storedVersion];
|
|
||||||
if ([[currentRow objectForKey: @"c_deleted"] intValue] > 0)
|
|
||||||
{
|
{
|
||||||
[self _purgeRecordWithName: _name];
|
/* run */
|
||||||
isNewRecord = YES;
|
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
|
else
|
||||||
isNewRecord = NO;
|
error = [NSException exceptionWithName:@"GCSStoreException"
|
||||||
|
reason:@"no content was provided"
|
||||||
|
userInfo:nil];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
error = [NSException exceptionWithName:@"GCSStoreException"
|
||||||
storedVersion = nil;
|
reason:@"no content filename was provided"
|
||||||
isNewRecord = YES;
|
userInfo:nil];
|
||||||
}
|
|
||||||
|
|
||||||
/* 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];
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue