]> err.no Git - sope/commitdiff
improved support for content revisions
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Wed, 13 Jul 2005 10:16:57 +0000 (10:16 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Wed, 13 Jul 2005 10:16:57 +0000 (10:16 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@891 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-gdl1/GDLContentStore/ChangeLog
sope-gdl1/GDLContentStore/GCSFolder.h
sope-gdl1/GDLContentStore/GCSFolder.m
sope-gdl1/GDLContentStore/Version

index 366167251e367f3743c18c3d4e6cb2409ecec6c7..e4a744841fa567fd623deffdf7ed88a74c397f87 100644 (file)
@@ -1,5 +1,9 @@
 2005-07-13  Helge Hess  <helge.hess@opengroupware.org>
 
+       * GCSFolder.m: added -writeContent:toName:baseVersion: to support
+         consistent update operations (eg using etags), properly increase
+         content object version on update operations (v4.5.31)
+         
        * GCSFolderManager.m, GCSFolder.m: changed not to use EOF
          attribute-name 'beautification', eg 'c_name' will stay 'c_name' 
          instead of being transformed into 'cName' (v4.5.30)
index 243b263f3242fc5dfeca40a12dc0e3aae7e8b10e..32d66dff69ea560ffb072a99a2cfe8c5c4316ae0 100644 (file)
@@ -99,6 +99,8 @@
 - (NSArray *)allSubFolderNames;
 
 - (NSString *)fetchContentWithName:(NSString *)_name;
+- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name
+  baseVersion:(unsigned int)_baseVersion;
 - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name;
 - (NSException *)deleteContentWithName:(NSString *)_name;
 
index ec6f15920ee1695bcd32dd1adb0e4ba1b3b97e23..c912e7e897ece7c7f36e8dd48a194dcdf60c73f4 100644 (file)
@@ -165,6 +165,25 @@ static GCSStringFormatter *stringFormatter = nil;
   return [[self channelManager] canConnect:[self quickLocation]];
 }
 
+/* errors */
+
+- (NSException *)errorVersionMismatchBetweenStoredVersion:(unsigned int)_store
+  andExpectedVersion:(unsigned int)_base
+{
+  NSDictionary *ui;
+
+  ui = [NSDictionary dictionaryWithObjectsAndKeys:
+                      [NSNumber numberWithUnsignedInt:_base],  
+                      @"GCSExpectedVersion",
+                      [NSNumber numberWithUnsignedInt:_store],
+                      @"GCSStoredVersion",
+                      nil];
+  
+  return [NSException exceptionWithName:@"GCSVersionMismatch"
+                     reason:@"Transaction conflict during a GCS modification."
+                     userInfo:ui];
+}
+
 /* operations */
 
 - (NSArray *)subFolderNames {
@@ -355,6 +374,7 @@ static GCSStringFormatter *stringFormatter = nil;
 - (NSString *)_generateUpdateStatementForRow:(NSDictionary *)_row
   tableName:(NSString *)_table
   whereColumn:(NSString *)_colname isEqualTo:(id)_value
+  andColumn:(NSString *)_colname2  isEqualTo:(id)_value2
 {
   // TODO: move to NSDictionary category?
   NSMutableString *sql;
@@ -369,11 +389,11 @@ static GCSStringFormatter *stringFormatter = nil;
   sql = [NSMutableString stringWithCapacity:512];
   [sql appendString:@"UPDATE "];
   [sql appendString:_table];
-
+  
   [sql appendString:@" SET "];
   for (i = 0, count = [keys count]; i < count; i++) {
     id value;
-
+    
     value = [_row objectForKey:[keys objectAtIndex:i]];
     value = [self _formatRowValue:value];
     
@@ -388,10 +408,19 @@ static GCSStringFormatter *stringFormatter = nil;
   [sql appendString:@" = "];
   [sql appendString:[self _formatRowValue:_value]];
   
+  if (_colname2 != nil) {
+    [sql appendString:@" AND "];
+    [sql appendString:_colname2];
+    [sql appendString:@" = "];
+    [sql appendString:[self _formatRowValue:_value2]];
+  }
+  
   return sql;
 }
 
-- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name {
+- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name
+  baseVersion:(unsigned int)_baseVersion
+{
   EOAdaptorChannel    *storeChannel, *quickChannel;
   NSMutableDictionary *quickRow, *contentRow;
   GCSFieldExtractor   *extractor;
@@ -429,6 +458,17 @@ static GCSStringFormatter *stringFormatter = nil;
     [self logWithFormat:@"  version: %@", storedVersion];
   isNewRecord = [storedVersion isNotNull] ? NO : 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 = [self->folderInfo quickExtractor];
@@ -452,7 +492,8 @@ static GCSStringFormatter *stringFormatter = nil;
     [contentRow setObject:[NSNumber numberWithInt:0] forKey:@"c_version"];
   else {
     // TODO: increase version?
-    [contentRow setObject:[NSNumber numberWithInt:[storedVersion intValue]]
+    [contentRow setObject:
+                 [NSNumber numberWithInt:([storedVersion intValue] + 1)]
                forKey:@"c_version"];
   }
   [contentRow setObject:_content forKey:@"c_content"];
@@ -486,20 +527,28 @@ static GCSStringFormatter *stringFormatter = nil;
     if (!self->ofFlags.sameTableForQuick) {
       qsql = [self _generateUpdateStatementForRow:quickRow
                   tableName:[self quickTableName]
-                  whereColumn:@"c_name" isEqualTo:_name];
+                  whereColumn:@"c_name" isEqualTo:_name
+                  andColumn:nil isEqualTo:nil];
     }
+    
+    /* also ensure in the DB that the version keeps staying the same */
     bsql = [self _generateUpdateStatementForRow:contentRow
                 tableName:[self storeTableName]
-                whereColumn:@"c_name" isEqualTo:_name];
+                whereColumn:@"c_name" isEqualTo:_name
+                andColumn:(_baseVersion != 0 ? @"c_version" : nil)
+                isEqualTo:(_baseVersion != 0
+                           ? [NSNumber numberWithUnsignedInt:_baseVersion] 
+                           : nil)];
   }
   
   /* execute */
   // TODO: execute in transactions
-
+  
   if ((error = [storeChannel evaluateExpressionX:bsql]) != nil) {
     [self logWithFormat:@"ERROR(%s): cannot %s content '%@': %@", 
          __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update", bsql, error];
   }
+  // TODO: should check whether a row was actually updated?
   
   if (error == nil && qsql != nil) {
     if ((error = [quickChannel evaluateExpressionX:qsql]) != nil) {
@@ -531,6 +580,11 @@ static GCSStringFormatter *stringFormatter = nil;
   return error;
 }
 
+- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name {
+  /* this method does not check for concurrent writes */
+  return [self writeContent:_content toName:_name baseVersion:0];
+}
+
 - (NSException *)deleteContentWithName:(NSString *)_name {
   EOAdaptorChannel *storeChannel, *quickChannel;
   NSException *error;
index 0d34fc447b7b65c8f580c911dd70027266e15719..d6d80e149b8b45ed5edc14900530a9f8f2f1c86c 100644 (file)
@@ -2,7 +2,7 @@
 
 MAJOR_VERSION:=4
 MINOR_VERSION:=5
-SUBMINOR_VERSION:=30
+SUBMINOR_VERSION:=31
 
 # v4.5.29 requires libNGExtensions v4.5.161
 # v4.5.26 does not require libNGiCal anymore