diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index b3b3d218a..2277ac105 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -111,6 +111,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoMailObject+ActiveSync.h" #import +#import #include @@ -3323,6 +3324,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSString *tableName, *query; GCSSpecialQueries *queries; + if ([GCSFolderManager singleStoreMode]) + return; + [self folderTableURL]; cm = [GCSChannelManager defaultChannelManager]; diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 596a94afc..1b53d4f6d 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -1388,6 +1388,32 @@ _SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database URL could be set to something like: `postgresql://sogo:sogo@localhost:5432/sogo/sogo_alarms_folder` +|S |OCSStoreURL +|Parameter used to set the database URL so that SOGo can use to start +all content data. You must also set `OCSAclURL` and `OCSCacheFolderURL` +if you set this parameter. Using these parameters will allow SOGo to use +a total of nine database tables - and prevent SOGo from creating three +database tables per collection. + +For PostgresSQL, set the database URL to something like: +`postgresql://sogo:@localhost:5432/sogo/sogo_store`. + +|S |OCSAclURL +|Parameter used to set the database URL so that SOGo can use to start +all ACL data. You must also set `OCSStoreURL` and `OCSCacheFolderURL` +if you set this parameter. + +For PostgresSQL, set the database URL to something like: +`postgresql://sogo:@localhost:5432/sogo/sogo_acl`. + +|S |OCSCacheFolderURL +|Parameter used to set the database URL so that SOGo can use to start +all cache data. You must also set `OCSStoreURL` and `OCSAclURL` +if you set this parameter. + +For PostgresSQL, set the database URL to something like: +`postgresql://sogo:@localhost:5432/sogo/sogo_cache_folder`. + See the "EMail reminders" section in this document for more information. |======================================================================= diff --git a/Main/SOGo.h b/Main/SOGo.h index 02bce3c4c..7c41f1625 100644 --- a/Main/SOGo.h +++ b/Main/SOGo.h @@ -1,6 +1,5 @@ /* - Copyright (C) 2005-2010 Inverse inc. - Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2005-2016 Inverse inc. This file is part of SOGo. @@ -27,7 +26,6 @@ @class NSArray; @class NSDictionary; -@class NSMutableDictionary; @class SOGoCache; diff --git a/Main/SOGo.m b/Main/SOGo.m index c91c23c71..8c9777206 100644 --- a/Main/SOGo.m +++ b/Main/SOGo.m @@ -1,6 +1,5 @@ /* - Copyright (C) 2005-2014 Inverse inc. - Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2005-2016 Inverse inc. This file is part of SOGo @@ -24,6 +23,7 @@ #import #import #import +#import #import #import @@ -211,36 +211,94 @@ static BOOL debugLeaks; [cm releaseChannel: tc]; } +- (void) _checkQuickTableWithTypeName: typeName + withCm: (GCSChannelManager *) cm + tableURL: (NSString *) url +{ + GCSFolderType *type; + NSString *sql; + NSString *tableName; + EOAdaptorChannel *channel; + + channel = [cm acquireOpenChannelForURL: [NSURL URLWithString: url]]; + + tableName = [NSString stringWithFormat: @"sogo_quick_%@", typeName]; + sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@", + tableName]; + if ([channel evaluateExpressionX: sql]) + { + type = [GCSFolderType folderTypeWithName: typeName]; + if (type) + { + sql = [type sqlQuickCreateWithTableName: tableName]; + if (![channel evaluateExpressionX: sql]) + [self logWithFormat: @"sogo quick table %@ successfully created!", + tableName]; + } + } + else + [channel cancelFetch]; + + [cm releaseChannel:channel]; +} + + +// +// If OCSStoreURL is defined, we also check for OCSAclURL, OCSCacheFolderURL +// and we create the combined quick tables. +// - (BOOL) _checkMandatoryTables { GCSChannelManager *cm; GCSFolderManager *fm; - NSString *urlStrings[] = {@"SOGoProfileURL", @"OCSFolderInfoURL", nil}; - NSString **urlString; - NSString *value; + NSArray *urlStrings; + NSArray *quickTypeStrings; + NSString *tmp, *value; SOGoSystemDefaults *defaults; - BOOL ok; + NSEnumerator *e; + BOOL ok, combined; defaults = [SOGoSystemDefaults sharedSystemDefaults]; ok = YES; + + if ([GCSFolderManager singleStoreMode]) + { + urlStrings = [NSArray arrayWithObjects: @"SOGoProfileURL", @"OCSFolderInfoURL", @"OCSStoreURL", @"OCSAclURL", @"OCSCacheFolderURL", nil]; + quickTypeStrings = [NSArray arrayWithObjects: @"contact", @"appointment", nil]; + combined = YES; + } + else + { + urlStrings = [NSArray arrayWithObjects: @"SOGoProfileURL", @"OCSFolderInfoURL", nil]; + combined = NO; + } + cm = [GCSChannelManager defaultChannelManager]; - urlString = urlStrings; - while (ok && *urlString) + e = [urlStrings objectEnumerator]; + while (ok && (tmp = [e nextObject])) { - value = [defaults stringForKey: *urlString]; + value = [defaults stringForKey: tmp]; if (value) - { - [self _checkTableWithCM: cm tableURL: value andType: *urlString]; - urlString++; - } + [self _checkTableWithCM: cm tableURL: value andType: tmp]; else { - [self errorWithFormat: @"No value specified for '%@'", *urlString]; + [self errorWithFormat: @"No value specified for '%@'", tmp]; ok = NO; } } + if (combined) + { + e = [quickTypeStrings objectEnumerator]; + while ((tmp = [e nextObject])) + { + [self _checkQuickTableWithTypeName: tmp + withCm: cm + tableURL: [defaults stringForKey: @"OCSFolderInfoURL"]]; + } + } + if (ok) { fm = [GCSFolderManager defaultFolderManager]; diff --git a/NEWS b/NEWS index 074d25fcd..a2b9d7cf5 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ 3.0.3 (2016-03-dd) ------------------ +New features + - [core] new database structure options to make SOGo use a total of nine tables + Enhancements - [web] updated Angular Material to version 1.0.6 diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index de3dfadf1..f6d84aa97 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -1,8 +1,6 @@ /* MAPIStoreUserContext.m - this file is part of SOGo * - * Copyright (C) 2012 Inverse inc - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2012-2016 Inverse inc * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +33,7 @@ #import #import +#import #import #import #import @@ -339,6 +338,9 @@ static NSMapTable *contextsTable = nil; NSString *tableName, *query; GCSSpecialQueries *queries; + if ([GCSFolderManager singleStoreMode]) + return; + [self folderTableURL]; cm = [GCSChannelManager defaultChannelManager]; diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index 70696362d..5532e9b67 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -91,13 +91,6 @@ static GCSStringFormatter *stringFormatter = nil; NSEnumerator *fields; GCSFieldInfo *field; NSString *fieldName; - - if (![_loc isNotNull]) - { - [self errorWithFormat:@"missing quicktable parameter!"]; - [self release]; - return nil; - } if ((self = [super init])) { folderManager = [_fm retain]; @@ -190,12 +183,31 @@ static GCSStringFormatter *stringFormatter = nil; } - (NSURL *)location { + if ([GCSFolderManager singleStoreMode]) + return [folderManager storeLocation]; + return location; } - (NSURL *)quickLocation { + if ([GCSFolderManager singleStoreMode]) + { + NSString *baseURL; + NSRange range; + + baseURL = [[folderManager folderInfoLocation] absoluteString]; + range = [baseURL rangeOfString: @"/" options: NSBackwardsSearch]; + if (range.location != NSNotFound) + baseURL = [baseURL substringToIndex: range.location]; + + return [NSURL URLWithString: [NSString stringWithFormat: @"%@/%@", baseURL, [self quickTableName]]]; + } + return quickLocation; } - (NSURL *)aclLocation { + if ([GCSFolderManager singleStoreMode]) + return [folderManager aclLocation]; + return aclLocation; } @@ -211,6 +223,9 @@ static GCSStringFormatter *stringFormatter = nil; return [[self location] gcsTableName]; } - (NSString *)quickTableName { + if ([GCSFolderManager singleStoreMode]) + return [NSString stringWithFormat: @"sogo_quick_%@", [folderTypeName lowercaseString]]; + return [[self quickLocation] gcsTableName]; } - (NSString *)aclTableName { @@ -473,6 +488,16 @@ static GCSStringFormatter *stringFormatter = nil; } whereSql = [NSMutableArray array]; + if ([GCSFolderManager singleStoreMode]) + { + if (requirement == bothTableRequired) + [whereSql addObject: [NSString stringWithFormat: + @"(a.c_folder_id = %@ AND b.c_folder_id = %@)", + folderId, folderId]]; + else + [whereSql addObject: [NSString stringWithFormat: @"c_folder_id = %@", + folderId]]; + } if (qualifier) { whereString = [NSString stringWithFormat: @"(%@)", @@ -693,6 +718,8 @@ andAttribute: (EOAttribute *)_attribute [sql appendString:@"INSERT INTO "]; [sql appendString:_table]; [sql appendString:@" ("]; + if ([GCSFolderManager singleStoreMode]) + [sql appendString:@"c_folder_id, "]; for (i = 0, count = [keys count]; i < count; i++) { if (i != 0) [sql appendString:@", "]; @@ -700,6 +727,8 @@ andAttribute: (EOAttribute *)_attribute } [sql appendString:@") VALUES ("]; + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat:@"%@, ", folderId]; for (i = 0, count = [keys count]; i < count; i++) { fieldName = [keys objectAtIndex:i]; @@ -767,6 +796,8 @@ andAttribute: (EOAttribute *)_attribute } [sql appendString:@" WHERE "]; + if ([GCSFolderManager singleStoreMode]) + [sql appendString: [NSString stringWithFormat: @"c_folder_id = %@ AND ", folderId]]; [sql appendString:_colname]; [sql appendString:@" = "]; attribute = [self _attributeForColumn: _colname]; @@ -853,22 +884,39 @@ andAttribute: (EOAttribute *)_attribute attribute1 = [self _attributeForColumn: _colname]; if (_colname2 == nil) { - qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity - qualifierFormat: @"%A = %@", _colname, - [self _formatRowValue:_value - withAdaptor: _adaptor andAttribute: attribute1]]; + if ([GCSFolderManager singleStoreMode]) + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@ AND c_folder_id = %@", _colname, + [self _formatRowValue:_value + withAdaptor: _adaptor andAttribute: attribute1], folderId]; + else + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@", _colname, + [self _formatRowValue:_value + withAdaptor: _adaptor andAttribute: attribute1]]; } else { attribute2 = [self _attributeForColumn: _colname2]; - qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity - qualifierFormat: @"%A = %@ AND %A = %@", - _colname, - [self _formatRowValue:_value - withAdaptor: _adaptor andAttribute: attribute1], - _colname2, - [self _formatRowValue:_value2 - withAdaptor: _adaptor andAttribute: attribute2]]; + if ([GCSFolderManager singleStoreMode]) + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@ AND %A = %@ AND c_folder_id = %@", + _colname, + [self _formatRowValue:_value + withAdaptor: _adaptor andAttribute: attribute1], + _colname2, + [self _formatRowValue:_value2 + withAdaptor: _adaptor andAttribute: attribute2]]; + else + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@ AND %A = %@", + _colname, + [self _formatRowValue:_value + withAdaptor: _adaptor andAttribute: attribute1], + _colname2, + [self _formatRowValue:_value2 + withAdaptor: _adaptor andAttribute: attribute2], + folderId]; } return AUTORELEASE(qualifier); @@ -887,10 +935,17 @@ andAttribute: (EOAttribute *)_attribute table = [self storeTableName]; attribute = [self _attributeForColumn: @"c_name"]; - delSql = [NSString stringWithFormat: @"DELETE FROM %@" - @" WHERE c_name = %@", table, - [self _formatRowValue: recordName - withAdaptor: [adaptorCtx adaptor] + if ([GCSFolderManager singleStoreMode]) + delSql = [NSString stringWithFormat: @"DELETE FROM %@" + @" WHERE c_name = %@ AND c_folder_id = %@", table, + [self _formatRowValue: recordName + withAdaptor: [adaptorCtx adaptor] + andAttribute: attribute], folderId]; + else + delSql = [NSString stringWithFormat: @"DELETE FROM %@" + @" WHERE c_name = %@", table, + [self _formatRowValue: recordName + withAdaptor: [adaptorCtx adaptor] andAttribute: attribute]]; [channel evaluateExpressionX: delSql]; @@ -1170,6 +1225,8 @@ andAttribute: (EOAttribute *)_attribute delsql = [delsql stringByAppendingString: [self _formatRowValue:_name withAdaptor: [adaptorCtx adaptor] andAttribute: [self _attributeForColumn: @"c_name"]]]; + if ([GCSFolderManager singleStoreMode]) + delsql = [delsql stringByAppendingFormat:@" AND c_folder_id = %@", folderId]; if ((error = [storeChannel evaluateExpressionX:delsql]) != nil) { [self errorWithFormat: @"%s: cannot delete content '%@': %@", @@ -1182,6 +1239,8 @@ andAttribute: (EOAttribute *)_attribute delsql = [delsql stringByAppendingString: [self _formatRowValue:_name withAdaptor: [adaptorCtx adaptor] andAttribute: [self _attributeForColumn: @"c_name"]]]; + if ([GCSFolderManager singleStoreMode]) + delsql = [delsql stringByAppendingFormat:@" AND c_folder_id = %@", folderId]; if ((error = [quickChannel evaluateExpressionX:delsql]) != nil) { [self errorWithFormat: @"%s: cannot delete quick row '%@': %@", @@ -1229,18 +1288,24 @@ andAttribute: (EOAttribute *)_attribute if (!ofFlags.sameTableForQuick) [[quickChannel adaptorContext] beginTransaction]; [[storeChannel adaptorContext] beginTransaction]; - query = [NSString stringWithFormat: @"DELETE FROM %@", [self storeTableName]]; + if ([GCSFolderManager singleStoreMode]) + query = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_folder_id = %@", [self storeTableName], folderId]; + else + query = [NSString stringWithFormat: @"DELETE FROM %@", [self storeTableName]]; error = [storeChannel evaluateExpressionX:query]; if (error) [self errorWithFormat: @"%s: cannot delete content '%@': %@", __PRETTY_FUNCTION__, query, error]; else if (!ofFlags.sameTableForQuick) { - /* content row deleted, now delete the quick row */ + /* content row deleted, now delete the quick row */ + if ([GCSFolderManager singleStoreMode]) + query = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_folder_id = %@", [self quickTableName], folderId]; + else query = [NSString stringWithFormat: @"DELETE FROM %@", [self quickTableName]]; - error = [quickChannel evaluateExpressionX: query]; - if (error) - [self errorWithFormat: @"%s: cannot delete quick row '%@': %@", - __PRETTY_FUNCTION__, query, error]; + error = [quickChannel evaluateExpressionX: query]; + if (error) + [self errorWithFormat: @"%s: cannot delete quick row '%@': %@", + __PRETTY_FUNCTION__, query, error]; } /* release channels and return */ @@ -1271,17 +1336,26 @@ andAttribute: (EOAttribute *)_attribute [[channel adaptorContext] beginTransaction]; table = [self storeTableName]; if ([table length] > 0) { - delsql = [@"DROP TABLE " stringByAppendingString: table]; + if ([GCSFolderManager singleStoreMode]) + delsql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_folder_id = %@", table, folderId]; + else + delsql = [@"DROP TABLE " stringByAppendingString: table]; [channel evaluateExpressionX:delsql]; } table = [self quickTableName]; if ([table length] > 0) { - delsql = [@"DROP TABLE " stringByAppendingString: table]; + if ([GCSFolderManager singleStoreMode]) + delsql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_folder_id = %@", table, folderId]; + else + delsql = [@"DROP TABLE " stringByAppendingString: table]; [channel evaluateExpressionX:delsql]; } table = [self aclTableName]; if ([table length] > 0) { - delsql = [@"DROP TABLE " stringByAppendingString: table]; + if ([GCSFolderManager singleStoreMode]) + delsql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_folder_id = %@", table, folderId]; + else + delsql = [@"DROP TABLE " stringByAppendingString: table]; [channel evaluateExpressionX:delsql]; } @@ -1376,10 +1450,17 @@ andAttribute: (EOAttribute *)_attribute [sql appendString:@"SELECT c_uid, c_object, c_role"]; [sql appendString:@" FROM "]; [sql appendString:[self aclTableName]]; - + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat:@" WHERE c_folder_id = %@", folderId]; + if (qualifier != nil) { - [sql appendString:@" WHERE "]; - [sql appendString:[self _sqlForQualifier:qualifier]]; + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat:@" AND (%@)", [self _sqlForQualifier:qualifier]]; + else + { + [sql appendString:@" WHERE "]; + [sql appendString:[self _sqlForQualifier:qualifier]]; + } } if ([sortOrderings count] > 0) { [sql appendString:@" ORDER BY "]; @@ -1462,10 +1543,14 @@ andAttribute: (EOAttribute *)_attribute [sql appendString:[self aclTableName]]; qSql = [self _sqlForQualifier: [_fs qualifier]]; if (qSql) - [sql appendFormat:@" WHERE %@", qSql]; - - /* open channel */ + { + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat:@" WHERE c_folder_id = %@ AND (%@)", folderId, qSql]; + else + [sql appendFormat:@" WHERE %@", qSql]; + } + /* open channel */ if ((channel = [self acquireAclChannel]) == nil) { [self errorWithFormat:@"could not open acl channel!"]; return; @@ -1495,11 +1580,21 @@ andAttribute: (EOAttribute *)_attribute count = 0; - sqlString = [NSMutableString stringWithFormat: - @"SELECT COUNT(*) AS CNT FROM %@", - [self storeTableName]]; + if ([GCSFolderManager singleStoreMode]) + sqlString = [NSMutableString stringWithFormat: + @"SELECT COUNT(*) AS CNT FROM %@ WHERE c_folder_id = %@", + [self storeTableName], folderId]; + else + sqlString = [NSMutableString stringWithFormat: + @"SELECT COUNT(*) AS CNT FROM %@", + [self storeTableName]]; if (excludeDeleted) - [sqlString appendString: @" WHERE (c_deleted != 1 OR c_deleted IS NULL)"]; + { + if ([GCSFolderManager singleStoreMode]) + [sqlString appendString: @" AND (c_deleted != 1 OR c_deleted IS NULL)"]; + else + [sqlString appendString: @" WHERE (c_deleted != 1 OR c_deleted IS NULL)"]; + } channel = [self acquireStoreChannel]; if (channel) diff --git a/SOPE/GDLContentStore/GCSFolderManager.h b/SOPE/GDLContentStore/GCSFolderManager.h index bbe17c2c1..8ec5ecf69 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.h +++ b/SOPE/GDLContentStore/GCSFolderManager.h @@ -38,15 +38,28 @@ GCSChannelManager *channelManager; NSDictionary *nameToType; NSURL *folderInfoLocation; + NSURL *storeLocation; + NSURL *aclLocation; + NSURL *cacheFolderLocation; } ++ (BOOL) singleStoreMode; + (id)defaultFolderManager; -- (id)initWithFolderInfoLocation:(NSURL *)_url; +- (id)initWithFolderInfoLocation: (NSURL *)_infoUrl + andStoreLocation: (NSURL *)_storeUrl + andAclLocation: (NSURL *)_aclUrl + andCacheFolderLocation: (NSURL *)_cacheFolderUrl; /* accessors */ - (NSURL *)folderInfoLocation; - (NSString *)folderInfoTableName; +- (NSURL *)storeLocation; +- (NSString *)storeTableName; +- (NSURL *)aclLocation; +- (NSString *)aclTableName; +- (NSURL *)cacheFolderLocation; +- (NSString *)cacheFolderTableName; /* connection */ diff --git a/SOPE/GDLContentStore/GCSFolderManager.m b/SOPE/GDLContentStore/GCSFolderManager.m index 0fb2946ff..29de16251 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.m +++ b/SOPE/GDLContentStore/GCSFolderManager.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import @@ -74,6 +75,7 @@ static NSString *GCSPathRecordName = @"c_path"; static NSString *GCSGenericFolderTypeName = @"Container"; static const char *GCSPathColumnPattern = "c_path%i"; static NSCharacterSet *asciiAlphaNumericCS = nil; +static BOOL _singleStoreMode = NO; + (void) initialize { @@ -96,11 +98,24 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; @"abcdefghijklmnopqrstuvwxyz"]; [asciiAlphaNumericCS retain]; } + + if ([ud stringForKey: @"OCSStoreURL"] && + [ud stringForKey: @"OCSAclURL"] && + [ud stringForKey: @"OCSCacheFolderURL"]) + _singleStoreMode = YES; +} + ++ (BOOL) singleStoreMode +{ + return _singleStoreMode; } + (id)defaultFolderManager { NSString *s; - NSURL *url; + NSURL *infoUrl; + NSURL *storeUrl; + NSURL *aclUrl; + NSURL *cacheFolderUrl; if (!fm) { @@ -110,18 +125,66 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; __PRETTY_FUNCTION__); return nil; } - if ((url = [NSURL URLWithString:s]) == nil) { + if ((infoUrl = [NSURL URLWithString:s]) == nil) { NSLog(@"ERROR(%s): default 'OCSFolderInfoURL' is not a valid URL: '%@'", __PRETTY_FUNCTION__, s); return nil; } - if ((fm = [[self alloc] initWithFolderInfoLocation:url]) == nil) { - NSLog(@"ERROR(%s): could not create folder manager with URL: '%@'", - __PRETTY_FUNCTION__, [url absoluteString]); + + if (_singleStoreMode) + { + s = [[NSUserDefaults standardUserDefaults] stringForKey:@"OCSStoreURL"]; + if ([s length] == 0) { + NSLog(@"ERROR(%s): default 'OCSStoreURL' is not configured.", + __PRETTY_FUNCTION__); + return nil; + } + if ((storeUrl = [NSURL URLWithString:s]) == nil) { + NSLog(@"ERROR(%s): default 'OCSStoreURL' is not a valid URL: '%@'", + __PRETTY_FUNCTION__, s); + return nil; + } + s = [[NSUserDefaults standardUserDefaults] stringForKey:@"OCSAclURL"]; + if ([s length] == 0) { + NSLog(@"ERROR(%s): default 'OCSAclURL' is not configured.", + __PRETTY_FUNCTION__); + return nil; + } + if ((aclUrl = [NSURL URLWithString:s]) == nil) { + NSLog(@"ERROR(%s): default 'OCSAclURL' is not a valid URL: '%@'", + __PRETTY_FUNCTION__, s); + return nil; + } + s = [[NSUserDefaults standardUserDefaults] stringForKey:@"OCSCacheFolderURL"]; + if ([s length] == 0) { + NSLog(@"ERROR(%s): default 'OCSCacheFolderURL' is not configured.", + __PRETTY_FUNCTION__); + return nil; + } + if ((cacheFolderUrl = [NSURL URLWithString:s]) == nil) { + NSLog(@"ERROR(%s): default 'OCSCacheFolderURL' is not a valid URL: '%@'", + __PRETTY_FUNCTION__, s); + return nil; + } + } + else + { + storeUrl = nil; + aclUrl = nil; + cacheFolderUrl = nil; + } + + if ((fm = [[self alloc] initWithFolderInfoLocation: infoUrl + andStoreLocation: storeUrl + andAclLocation: aclUrl + andCacheFolderLocation: cacheFolderUrl]) == nil) { + NSLog(@"ERROR(%s): could not create folder manager with URLs: '%@', '%@', '%@'", + __PRETTY_FUNCTION__, [infoUrl absoluteString], + [storeUrl absoluteString], [aclUrl absoluteString]); return nil; } if (debugOn) - [self debugWithFormat:@"Note: setup default manager at: %@", url]; + [self debugWithFormat:@"Note: setup default manager at: %@", infoUrl]; } return fm; @@ -159,20 +222,81 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; return typeMap; } -- (id)initWithFolderInfoLocation:(NSURL *)_url { - if (_url == nil) { +- (id)initWithFolderInfoLocation: (NSURL *)_infoUrl + andStoreLocation: (NSURL *)_storeUrl + andAclLocation: (NSURL *)_aclUrl + andCacheFolderLocation: (NSURL *)_cacheFolderUrl { + if (_infoUrl == nil) { [self logWithFormat:@"ERROR(%s): missing folder info url!", __PRETTY_FUNCTION__]; [self release]; return nil; } + + if (_singleStoreMode) + { + if (_storeUrl == nil) { + [self logWithFormat:@"ERROR(%s): missing folder store url!", + __PRETTY_FUNCTION__]; + [self release]; + return nil; + } + if (_aclUrl == nil) { + [self logWithFormat:@"ERROR(%s): missing folder acl url!", + __PRETTY_FUNCTION__]; + [self release]; + return nil; + } + if (_cacheFolderUrl == nil) { + [self logWithFormat:@"ERROR(%s): missing cache folder url!", + __PRETTY_FUNCTION__]; + [self release]; + return nil; + } + + } + if ((self = [super init])) { channelManager = [[GCSChannelManager defaultChannelManager] retain]; - folderInfoLocation = [_url retain]; + folderInfoLocation = [_infoUrl retain]; + + if (_singleStoreMode) + { + storeLocation = [_storeUrl retain]; + aclLocation = [_aclUrl retain]; + cacheFolderLocation = [_cacheFolderUrl retain]; + } + else + { + storeLocation = nil; + aclLocation = nil; + cacheFolderLocation = nil; + } if ([[self folderInfoTableName] length] == 0) { - [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", - __PRETTY_FUNCTION__, [_url absoluteString]]; + [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", + __PRETTY_FUNCTION__, [_infoUrl absoluteString]]; + [self release]; + return nil; + } + + if (_singleStoreMode && [[self storeTableName] length] == 0) { + [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", + __PRETTY_FUNCTION__, [_storeUrl absoluteString]]; + [self release]; + return nil; + } + + if (_singleStoreMode && [[self aclTableName] length] == 0) { + [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", + __PRETTY_FUNCTION__, [_aclUrl absoluteString]]; + [self release]; + return nil; + } + + if (_singleStoreMode && [[self cacheFolderTableName] length] == 0) { + [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", + __PRETTY_FUNCTION__, [_cacheFolderUrl absoluteString]]; [self release]; return nil; } @@ -201,6 +325,31 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; return [[self folderInfoLocation] gcsTableName]; } +- (NSURL *)storeLocation { + return storeLocation; +} + +- (NSString *)storeTableName { + return [[self storeLocation] gcsTableName]; +} + +- (NSURL *)aclLocation { + return aclLocation; +} + +- (NSString *)aclTableName { + return [[self aclLocation] gcsTableName]; +} + +- (NSURL *)cacheFolderLocation { + return cacheFolderLocation; +} + +- (NSString *)cacheFolderTableName { + return [[self cacheFolderLocation] gcsTableName]; +} + + /* connection */ - (GCSChannelManager *)channelManager { @@ -266,7 +415,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; GCSFolderType *folderType; NSString *folderTypeName, *locationString, *folderName, *path; NSNumber *folderId; - NSURL *location, *quickLocation, *aclLocation; + NSURL *location, *quickLocation, *acl_location; if (_record == nil) return nil; @@ -309,7 +458,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; } locationString = [_record objectForKey:@"c_acl_location"]; - aclLocation = [locationString isNotNull] + acl_location = [locationString isNotNull] ? [NSURL URLWithString:locationString] : nil; @@ -317,7 +466,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; folderTypeName:folderTypeName folderType:folderType location:location quickLocation:quickLocation - aclLocation:aclLocation + aclLocation:acl_location folderManager:self]; return [folder autorelease]; } @@ -766,7 +915,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; baseURL, aclTableName, folderType]; error = [channel evaluateExpressionX: sql]; - if (!error) + if (!_singleStoreMode && !error) { specialQuery = [channel specialQueries]; createQuery = [specialQuery createFolderTableWithName: tableName]; diff --git a/SOPE/GDLContentStore/GCSFolderType.m b/SOPE/GDLContentStore/GCSFolderType.m index a4de5832b..ce91f3cc5 100644 --- a/SOPE/GDLContentStore/GCSFolderType.m +++ b/SOPE/GDLContentStore/GCSFolderType.m @@ -31,6 +31,7 @@ #import +#import "GCSFolderManager.h" #import "GCSFolderType.h" #import "GCSFolder.h" #import "GCSFieldInfo.h" @@ -157,15 +158,52 @@ { NSMutableString *sql; unsigned i, count; - + GCSFieldInfo *field; + BOOL combined; + + combined = NO; + + if ([GCSFolderManager singleStoreMode]) + combined = YES; + sql = [NSMutableString stringWithFormat: @"CREATE TABLE %@ (", _tabName]; + + if (combined) + [sql appendString: @"c_folder_id INT NOT NULL, "]; + count = [quickFields count]; for (i = 0; i < count; i++) { - if (i > 0) [sql appendString:@", "]; - [sql appendFormat: @" %@", [[quickFields objectAtIndex:i] sqlCreateSection]]; + field = [quickFields objectAtIndex:i]; + + if (i != 0) [sql appendString: @", "]; + [sql appendString:[field columnName]]; + [sql appendString:@" "]; + [sql appendString:[field sqlType]]; + + [sql appendString:@" "]; + if (![field doesAllowNull]) [sql appendString:@"NOT "]; + [sql appendString:@"NULL"]; + + if (!combined && [field isPrimaryKey]) + [sql appendString:@" PRIMARY KEY"]; } - [sql appendString:@"\n)"]; + + // Define primary key constraint for c_folder_id and additional primary key fields + if (combined) + { + [sql appendFormat:@", CONSTRAINT %@_pkey PRIMARY KEY(c_folder_id", _tabName]; + for (i = 0; i < count; i++) + { + field = [quickFields objectAtIndex:i]; + if ([field isPrimaryKey]) + [sql appendFormat:@", %@", [field columnName]]; + } + + [sql appendString:@")"]; + } + + [sql appendString:@")"]; return sql; } diff --git a/Scripts/sql-update-3.0.0-to-combined-mysql.sh b/Scripts/sql-update-3.0.0-to-combined-mysql.sh new file mode 100755 index 000000000..d51243a4c --- /dev/null +++ b/Scripts/sql-update-3.0.0-to-combined-mysql.sh @@ -0,0 +1,200 @@ +#!/bin/bash + +STOREFIELDS="c_name, c_content, c_creationdate, c_lastmodified, c_version, c_deleted" +APPOINTMENTFIELDS="c_name, c_uid, c_startdate, c_enddate, c_cycleenddate, c_title, c_participants, c_isallday, c_iscycle, c_cycleinfo, \ +c_classification, c_isopaque, c_status, c_priority, c_location, c_orgmail, c_partmails, c_partstates, c_category, c_sequence, c_component, c_nextalarm, c_description" +CONTACTFIELDS="c_name, c_givenname, c_cn, c_sn, c_screenname, c_l, c_mail, c_o, c_ou, c_telephonenumber, c_categories, c_component" + +IFS=" " + +# Parse postgres connection string from OCSFolderInfoURL in sogo.conf +set $(sogo-tool dump-defaults -f /etc/sogo/sogo.conf | awk -F\" '/ OCSFolderInfoURL =/ {print $2}' \ + | sed -n 's/\([^:]\+\):\/\/\([^:]\+\):\([^@]\+\)@\([^:]\+\):\([^/]\+\)\/\([^/]\+\)\/\([^/]\+\)/\1 \2 \3 \4 \5 \6 \7/p') +PROTOCOL=$1 +USER=$2 +PWD=$3 +HOST=$4 +PORT=$5 +DB=$6 +TABLE=$7 + +if [ -z "$PROTOCOL" ] || [ -z "$USER" ] || [ -z "$HOST" ] || [ -z "$PORT" ] || [ -z "$DB" ] || [ -z "$TABLE" ]; then + echo "ERROR: Failed to parse value of OCSFolderInfoURL in /etc/sogo/sogo.conf" 1>&2 + exit 1 +fi + +if ! [ "$PROTOCOL" = "mysql" ]; then + echo "ERROR: Unsupported protocol $PROTOCOL. Use this script for migrating mysql databases." 1>&2 + exit 1 +fi + +# Create temporary files +OPTSFILE=$(mktemp) +TABLEFILE=$(mktemp) +SQLFILE=$(mktemp) +trap "rm -rf $TABLEFILE $OPTSFILE $SQLFILE" EXIT + +# Save password for subsequent batch-mode calls of mysql +cat > $OPTSFILE <&2 + exit 1 +fi + +if ! [ "$CHECK" = "1" ]; then + cat >> $SQLFILE < $TABLEFILE +RET=$? + +if [ $RET -ne 0 ]; then + echo "ERROR: mysql returned error $RET" 1>&2 + exit 1 +fi + +cat $TABLEFILE | sed "s/[[:space:]]\+/ /g" | while read LINE +do + set $LINE + FOLDERID=$2 + FOLDERTYPE=$3 + QUICKTABLE=$4 + STORETABLE=$5 + ACLTABLE=$6 + + if [ "$FOLDERTYPE" != "Appointment" ] && [ "$FOLDERTYPE" != "Contact" ]; then + echo "ERROR: Unknown folder type $FOLDERTYPE, folder id $FOLDERID" 1>&2 + exit 1 + fi + + # Merge content and acl + echo "INSERT INTO sogo_store(c_folder_id, $STOREFIELDS) SELECT $FOLDERID, $STOREFIELDS FROM $STORETABLE;" >> $SQLFILE + echo "INSERT INTO sogo_acl(c_folder_id, c_object, c_uid, c_role) SELECT $FOLDERID, c_object, c_uid, c_role FROM $ACLTABLE;" >> $SQLFILE + + # Merge quick table + if [ "$FOLDERTYPE" = "Appointment" ]; then + echo "INSERT INTO sogo_quick_appointment(c_folder_id, $APPOINTMENTFIELDS) SELECT $FOLDERID, $APPOINTMENTFIELDS FROM $QUICKTABLE;" >> $SQLFILE + else + echo "INSERT INTO sogo_quick_contact(c_folder_id, $CONTACTFIELDS) SELECT $FOLDERID, $CONTACTFIELDS FROM $QUICKTABLE;" >> $SQLFILE + fi + + # Drop migrated tables and update folder info + echo "DROP TABLE $QUICKTABLE;" >> $SQLFILE + echo "DROP TABLE $STORETABLE;" >> $SQLFILE + echo "DROP TABLE $ACLTABLE;" >> $SQLFILE + echo "UPDATE sogo_folder_info SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE + echo >> $SQLFILE +done + +echo "Merging tables...." +mysql --defaults-file="$OPTSFILE" -u $USER -h $HOST -P $PORT $DB -B -N < $SQLFILE +RET=$? + +if [ $RET -ne 0 ]; then + echo "ERROR: mysql returned error $RET" 1>&2 + exit 1 +fi + + +######################### +# Patch sogo.conf + +if ! (grep -q "OCSStoreURL" /etc/sogo/sogo.conf); then + echo "Patching /etc/sogo/sogo.conf...." + # Generate properties OCSStoreURL and OCSAclURL + sed "s/\(.*\)OCSFolderInfoURL.*$/\0\n\1OCSStoreURL = \"mysql:\/\/$USER:$PASSWORD@$HOST:$PORT\/$DB\/sogo_store\";\ +\n\1OCSAclURL = \"mysql:\/\/$USER:$PASSWORD@$HOST:$PORT\/$DB\/sogo_acl\";/g" -i /etc/sogo/sogo.conf +fi + + diff --git a/Scripts/sql-update-3.0.0-to-combined.sh b/Scripts/sql-update-3.0.0-to-combined.sh new file mode 100755 index 000000000..6d33635cc --- /dev/null +++ b/Scripts/sql-update-3.0.0-to-combined.sh @@ -0,0 +1,198 @@ +#!/bin/bash + +STOREFIELDS="c_name, c_content, c_creationdate, c_lastmodified, c_version, c_deleted" +APPOINTMENTFIELDS="c_name, c_uid, c_startdate, c_enddate, c_cycleenddate, c_title, c_participants, c_isallday, c_iscycle, c_cycleinfo, \ +c_classification, c_isopaque, c_status, c_priority, c_location, c_orgmail, c_partmails, c_partstates, c_category, c_sequence, c_component, c_nextalarm, c_description" +CONTACTFIELDS="c_name, c_givenname, c_cn, c_sn, c_screenname, c_l, c_mail, c_o, c_ou, c_telephonenumber, c_categories, c_component" + +IFS=" " + +# Parse postgres connection string from OCSFolderInfoURL in sogo.conf +set $(sogo-tool dump-defaults -f /etc/sogo/sogo.conf | awk -F\" '/ OCSFolderInfoURL =/ {print $2}' \ + | sed -n 's/\([^:]\+\):\/\/\([^:]\+\):\([^@]\+\)@\([^:]\+\):\([^/]\+\)\/\([^/]\+\)\/\([^/]\+\)/\1 \2 \3 \4 \5 \6 \7/p') +PROTOCOL=$1 +USER=$2 +PWD=$3 +HOST=$4 +PORT=$5 +DB=$6 +TABLE=$7 + +if [ -z "$PROTOCOL" ] || [ -z "$USER" ] || [ -z "$HOST" ] || [ -z "$PORT" ] || [ -z "$DB" ] || [ -z "$TABLE" ]; then + echo "ERROR: Failed to parse value of OCSFolderInfoURL in /etc/sogo/sogo.conf" 1>&2 + exit 1 +fi + +if ! [ "$PROTOCOL" = "postgresql" ]; then + echo "ERROR: Unsupported protocol $PROTOCOL. Use this script for migrating postgresql databases." 1>&2 + exit 1 +fi + +# Create temporary files +export PGPASSFILE=$(mktemp) +TABLEFILE=$(mktemp) +SQLFILE=$(mktemp) +trap "rm -rf $TABLEFILE $PGPASSFILE $SQLFILE" EXIT + +# Save password for subsequent batch-mode calls of psql +echo "*:*:*:$USER:$PWD" > $PGPASSFILE + + +######################### +# Create new tables + +# Check if table sogo_store exists +CHECK=$(psql -A -F " " -w -t -U $USER -h $HOST $DB -c "SELECT TRUE FROM information_schema.tables WHERE table_name='sogo_store'") +RET=$? + +if [ $RET -ne 0 ]; then + echo "ERROR: postgresql returned error $RET" 1>&2 + exit 1 +fi + +if [ "$CHECK" != "t" ]; then + cat >> $SQLFILE < $TABLEFILE +RET=$? + +if [ $RET -ne 0 ]; then + echo "ERROR: postgresql returned error $RET" 1>&2 + exit 1 +fi + +while read LINE +do + set $LINE + FOLDERID=$2 + FOLDERTYPE=$3 + QUICKTABLE=$4 + STORETABLE=$5 + ACLTABLE=$6 + + if [ "$FOLDERTYPE" != "Appointment" ] && [ "$FOLDERTYPE" != "Contact" ]; then + echo "ERROR: Unknown folder type $FOLDERTYPE, folder id $FOLDERID" 1>&2 + exit 1 + fi + + # Merge content and acl + echo "INSERT INTO sogo_store(c_folder_id, $STOREFIELDS) SELECT $FOLDERID, $STOREFIELDS FROM $STORETABLE;" >> $SQLFILE + echo "INSERT INTO sogo_acl(c_folder_id, c_object, c_uid, c_role) SELECT $FOLDERID, c_object, c_uid, c_role FROM $ACLTABLE;" >> $SQLFILE + + # Merge quick table + if [ "$FOLDERTYPE" = "Appointment" ]; then + echo "INSERT INTO sogo_quick_appointment(c_folder_id, $APPOINTMENTFIELDS) SELECT $FOLDERID, $APPOINTMENTFIELDS FROM $QUICKTABLE;" >> $SQLFILE + else + echo "INSERT INTO sogo_quick_contact(c_folder_id, $CONTACTFIELDS) SELECT $FOLDERID, $CONTACTFIELDS FROM $QUICKTABLE;" >> $SQLFILE + fi + + # Drop migrated tables and update folder info + echo "DROP TABLE $QUICKTABLE;" >> $SQLFILE + echo "DROP TABLE $STORETABLE;" >> $SQLFILE + echo "DROP TABLE $ACLTABLE;" >> $SQLFILE + echo "UPDATE sogo_folder_info SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE + echo >> $SQLFILE +done < $TABLEFILE + +echo "Merging tables...." +psql -v ON_ERROR_STOP=1 -w -U $USER -h $HOST $DB < $SQLFILE +RET=$? + +if [ $RET -ne 0 ]; then + echo "ERROR: postgresql returned error $RET" 1>&2 + exit 1 +fi + + +######################### +# Patch sogo.conf + +if ! (grep -q "OCSStoreURL" /etc/sogo/sogo.conf); then + echo "Patching /etc/sogo/sogo.conf...." + # Generate properties OCSStoreURL and OCSAclURL + sed "s/\(.*\)OCSFolderInfoURL.*$/\0\n\1OCSStoreURL = \"postgresql:\/\/$USER:$PASSWORD@$HOST:$PORT\/$DB\/sogo_store\";\ +\n\1OCSAclURL = \"postgresql:\/\/$USER:$PASSWORD@$HOST:$PORT\/$DB\/sogo_acl\";/g" -i /etc/sogo/sogo.conf +fi + + diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.h b/SoObjects/SOGo/SOGoCacheGCSFolder.h index c51dd598f..56fe8c3d3 100644 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.h +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.h @@ -1,6 +1,6 @@ /* SOGoCacheGCSFolder.h - this file is part of SOGo * - * Copyright (C) 2012-2014 Inverse inc. + * Copyright (C) 2012-2016 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.h b/SoObjects/SOGo/SOGoCacheGCSObject.h index a2ffe5bf0..80d21a645 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.h +++ b/SoObjects/SOGo/SOGoCacheGCSObject.h @@ -1,6 +1,6 @@ /* SOGoCacheGCSObject.h - this file is part of SOGo * - * Copyright (C) 2012-2014 Inverse inc + * Copyright (C) 2012-2016 Inverse inc * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index d0b8908a8..603917128 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -1,6 +1,6 @@ /* SOGoCacheGCSObject.m - this file is part of SOGo * - * Copyright (C) 2012-2014 Inverse inc + * Copyright (C) 2012-2016 Inverse inc * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,9 @@ #import #import #import +#import #import +#import #import #import @@ -124,6 +126,16 @@ static EOAttribute *textColumn = nil; - (NSURL *) tableUrl { + NSString *s; + + s = [[NSUserDefaults standardUserDefaults] stringForKey: @"OCSCacheFolderURL"]; + + if (s) + { + tableUrl = [NSURL URLWithString: s]; + [tableUrl retain]; + } + if (!tableUrl) { tableUrl = [container tableUrl]; @@ -247,6 +259,10 @@ static EOAttribute *textColumn = nil; [self tableName], newPath]; [sql appendFormat: @" WHERE c_path = '%@'", oldPath]; + + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]]; + [self performBatchSQLQueries: [NSArray arrayWithObject: sql]]; } } @@ -275,6 +291,10 @@ static EOAttribute *textColumn = nil; else [sql appendString: @", c_parent_path = NULL"]; [sql appendFormat: @" WHERE c_path = '%@'", oldPath]; + + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]]; + [self performBatchSQLQueries: [NSArray arrayWithObject: sql]]; } @@ -379,6 +399,10 @@ static EOAttribute *textColumn = nil; sql = [NSMutableString stringWithFormat: @"SELECT * FROM %@ WHERE c_path = %@", tableName, pathValue]; + + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]]; + if (startVersion > -1) [sql appendFormat: @" AND c_version > %d", (int)startVersion]; @@ -413,6 +437,9 @@ static EOAttribute *textColumn = nil; sql = [NSMutableString stringWithFormat: @"SELECT * FROM %@ WHERE c_type = %d AND c_deleted <> 1", tableName, objectType]; + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]]; + if (startVersion > -1) [sql appendFormat: @" AND c_version > %d", (int)startVersion]; @@ -478,8 +505,9 @@ static EOAttribute *textColumn = nil; - (NSException *) destroy { - NSString *tableName, *pathValue, *sql; + NSString *tableName, *pathValue; EOAdaptorChannel *channel; + NSMutableString *sql; GCSChannelManager *cm; NSException *result; EOAdaptor *adaptor; @@ -493,11 +521,14 @@ static EOAttribute *textColumn = nil; forAttribute: textColumn]; result = nil; - sql = [NSString stringWithFormat: - (@"DELETE FROM %@" - @" WHERE c_path = %@"), - tableName, - pathValue]; + sql = [NSMutableString stringWithFormat: + (@"DELETE FROM %@" + @" WHERE c_path = %@"), + tableName, + pathValue]; + + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]]; result = [channel evaluateExpressionX: sql]; @@ -512,14 +543,14 @@ static EOAttribute *textColumn = nil; - (void) save { - NSString *sql; + NSMutableString *sql; NSData *content; NSCalendarDate *now; GCSChannelManager *cm; EOAdaptor *adaptor; EOAdaptorChannel *channel; NSInteger creationDateValue, lastModifiedValue, deletedValue; - NSString *tableName, *pathValue, *parentPathValue, *propsValue; + NSString *tableName, *loginValue, *pathValue, *parentPathValue, *propsValue; NSException *result; if (!initialized) @@ -538,6 +569,8 @@ static EOAttribute *textColumn = nil; adaptor = [[channel adaptorContext] adaptor]; pathValue = [adaptor formatValue: [self path] forAttribute: textColumn]; + loginValue = [adaptor formatValue: [[context activeUser] login] + forAttribute: textColumn]; lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970]; @@ -563,30 +596,46 @@ static EOAttribute *textColumn = nil; forAttribute: textColumn]; if (!parentPathValue) parentPathValue = @"NULL"; - sql = [NSString stringWithFormat: - (@"INSERT INTO %@" - @" (c_path, c_parent_path, c_type, c_creationdate, c_lastmodified," - @" c_deleted, c_version, c_content)" - @" VALUES (%@, %@, %d, %d, %d, 0, 0, %@" - @")"), - tableName, - pathValue, parentPathValue, objectType, - (int)creationDateValue, (int)lastModifiedValue, - propsValue]; + + if ([GCSFolderManager singleStoreMode]) + sql = [NSString stringWithFormat: + (@"INSERT INTO %@" + @" (c_uid, c_path, c_parent_path, c_type, c_creationdate, c_lastmodified," + @" c_deleted, c_version, c_content)" + @" VALUES (%@, %@, %@, %d, %d, %d, 0, 0, %@" + @")"), + tableName, + loginValue, pathValue, parentPathValue, objectType, + (int)creationDateValue, (int)lastModifiedValue, + propsValue]; + else + sql = [NSString stringWithFormat: + (@"INSERT INTO %@" + @" (c_path, c_parent_path, c_type, c_creationdate, c_lastmodified," + @" c_deleted, c_version, c_content)" + @" VALUES (%@, %@, %d, %d, %d, 0, 0, %@" + @")"), + tableName, + pathValue, parentPathValue, objectType, + (int)creationDateValue, (int)lastModifiedValue, + propsValue]; isNew = NO; } else { version++; deletedValue = (deleted ? 1 : 0); - sql = [NSString stringWithFormat: - (@"UPDATE %@" - @" SET c_lastmodified = %d, c_deleted = %d," - @" c_version = %d, c_content = %@" - @" WHERE c_path = %@"), - tableName, - (int)lastModifiedValue, (int)deletedValue, (int)version, propsValue, - pathValue]; + sql = [NSMutableString stringWithFormat: + (@"UPDATE %@" + @" SET c_lastmodified = %d, c_deleted = %d," + @" c_version = %d, c_content = %@" + @" WHERE c_path = %@"), + tableName, + (int)lastModifiedValue, (int)deletedValue, (int)version, propsValue, + pathValue]; + + if ([GCSFolderManager singleStoreMode]) + [sql appendFormat: @" AND c_uid = %@", loginValue]; } result = [channel evaluateExpressionX: sql]; diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index f80afb076..c60eb348e 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -1778,11 +1778,18 @@ static NSArray *childRecordFields = nil; userRoles = [roles objectEnumerator]; while ((currentRole = [userRoles nextObject])) { - SQL = [NSString stringWithFormat: @"INSERT INTO %@" - @" (c_object, c_uid, c_role)" - @" VALUES ('/%@', '%@', '%@')", - [folder aclTableName], - objectPath, uid, currentRole]; + if ([GCSFolderManager singleStoreMode]) + SQL = [NSString stringWithFormat: @"INSERT INTO %@" + @" (c_object, c_uid, c_role, c_folder_id)" + @" VALUES ('/%@', '%@', '%@', %@)", + [folder aclTableName], + objectPath, uid, currentRole, [folder folderId]]; + else + SQL = [NSString stringWithFormat: @"INSERT INTO %@" + @" (c_object, c_uid, c_role)" + @" VALUES ('/%@', '%@', '%@')", + [folder aclTableName], + objectPath, uid, currentRole]; [channel evaluateExpressionX: SQL]; } diff --git a/UI/MainUI/GNUmakefile b/UI/MainUI/GNUmakefile index fba05714e..210cf9483 100644 --- a/UI/MainUI/GNUmakefile +++ b/UI/MainUI/GNUmakefile @@ -26,6 +26,12 @@ MainUI_RESOURCE_FILES += \ SOGoProfile-oracle.sql \ OCSFolderInfo.sql \ OCSFolderInfo-oracle.sql \ + OCSStore.sql \ + OCSStore-mysql.sql \ + OCSAcl-mysql.sql \ + OCSAcl-postgresql.sql \ + OCSCacheFolder-mysql.sql \ + OCSCacheFolder-postgresql.sql MainUI_LOCALIZED_RESOURCE_FILES += \ Locale Localizable.strings diff --git a/UI/MainUI/OCSAcl-mysql.sql b/UI/MainUI/OCSAcl-mysql.sql new file mode 100644 index 000000000..1cec83bc6 --- /dev/null +++ b/UI/MainUI/OCSAcl-mysql.sql @@ -0,0 +1,13 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_folder_id INTEGER NOT NULL, + c_object VARCHAR(255) NOT NULL, + c_uid VARCHAR(255) NOT NULL, + c_role VARCHAR(80) NOT NULL, + INDEX @{tableName}_c_folder_id_idx(c_folder_id), + INDEX @{tableName}_c_uid_idx(c_uid) +); diff --git a/UI/MainUI/OCSAcl-postgresql.sql b/UI/MainUI/OCSAcl-postgresql.sql new file mode 100644 index 000000000..fdf8af506 --- /dev/null +++ b/UI/MainUI/OCSAcl-postgresql.sql @@ -0,0 +1,14 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_folder_id INTEGER NOT NULL, + c_object VARCHAR(255) NOT NULL, + c_uid VARCHAR(255) NOT NULL, + c_role VARCHAR(80) NOT NULL +); + +CREATE INDEX @{tableName}_c_folder_id_idx ON @{tableName}(c_folder_id); +CREATE INDEX @{tableName}_c_uid_idx ON @{tableName}(c_uid); diff --git a/UI/MainUI/OCSCacheFolder-mysql.sql b/UI/MainUI/OCSCacheFolder-mysql.sql new file mode 100644 index 000000000..fe8a3e3e9 --- /dev/null +++ b/UI/MainUI/OCSCacheFolder-mysql.sql @@ -0,0 +1,17 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_uid VARCHAR(255) NOT NULL, + c_path VARCHAR(255) NOT NULL, + c_parent_path VARCHAR(255), + c_type TINYINT UNSIGNED NOT NULL, + c_creationdate INT NOT NULL, + c_lastmodified INT NOT NULL, + c_version INT NOT NULL DEFAULT 0, + c_deleted TINYINT NOT NULL DEFAULT 0, + c_content LONGTEXT, + CONSTRAINT @{tableName}_pkey PRIMARY KEY (c_uid, c_path) +); diff --git a/UI/MainUI/OCSCacheFolder-postgresql.sql b/UI/MainUI/OCSCacheFolder-postgresql.sql new file mode 100644 index 000000000..1515eb171 --- /dev/null +++ b/UI/MainUI/OCSCacheFolder-postgresql.sql @@ -0,0 +1,17 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_uid VARCHAR(255) NOT NULL, + c_path VARCHAR(255) NOT NULL, + c_parent_path VARCHAR(255), + c_type SMALLINT NOT NULL, + c_creationdate INT NOT NULL, + c_lastmodified INT NOT NULL, + c_version INT NOT NULL DEFAULT 0, + c_deleted SMALLINT NOT NULL DEFAULT 0, + c_content TEXT, + CONSTRAINT @{tableName}_pkey PRIMARY KEY (c_uid, c_path) +); diff --git a/UI/MainUI/OCSFolderInfo-oracle.sql b/UI/MainUI/OCSFolderInfo-oracle.sql index 16750692d..0652a8dc6 100644 --- a/UI/MainUI/OCSFolderInfo-oracle.sql +++ b/UI/MainUI/OCSFolderInfo-oracle.sql @@ -10,7 +10,7 @@ CREATE TABLE @{tableName} ( c_path3 VARCHAR(255) NULL, -- parts (for fast queries) c_path4 VARCHAR(255) NULL, -- parts (for fast queries) c_foldername VARCHAR(255) NOT NULL, -- last path component - c_location VARCHAR(2048) NOT NULL, -- URL to folder + c_location VARCHAR(2048) NULL, -- URL to folder c_quick_location VARCHAR(2048) NULL, -- URL to quicktable of folder c_acl_location VARCHAR(2048) NULL, -- URL to quicktable of folder c_folder_type VARCHAR(255) NOT NULL -- the folder type ... diff --git a/UI/MainUI/OCSFolderInfo.sql b/UI/MainUI/OCSFolderInfo.sql index 80c2c2649..042b66c13 100644 --- a/UI/MainUI/OCSFolderInfo.sql +++ b/UI/MainUI/OCSFolderInfo.sql @@ -11,7 +11,7 @@ CREATE TABLE @{tableName} ( c_path3 VARCHAR(255) NULL, -- parts (for fast queries) c_path4 VARCHAR(255) NULL, -- parts (for fast queries) c_foldername VARCHAR(255) NOT NULL, -- last path component - c_location VARCHAR(2048) NOT NULL, -- URL to folder + c_location VARCHAR(2048) NULL, -- URL to folder c_quick_location VARCHAR(2048) NULL, -- URL to quicktable of folder c_acl_location VARCHAR(2048) NULL, -- URL to quicktable of folder c_folder_type VARCHAR(255) NOT NULL -- the folder type ... diff --git a/UI/MainUI/OCSStore-mysql.sql b/UI/MainUI/OCSStore-mysql.sql new file mode 100644 index 000000000..201160a75 --- /dev/null +++ b/UI/MainUI/OCSStore-mysql.sql @@ -0,0 +1,15 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_folder_id INT NOT NULL, + c_name VARCHAR (255), + c_content MEDIUMTEXT NOT NULL, + c_creationdate INT NOT NULL, + c_lastmodified INT NOT NULL, + c_version INT NOT NULL, + c_deleted INT NULL, + CONSTRAINT @{tableName}_pkey PRIMARY KEY (c_folder_id, c_name) +); diff --git a/UI/MainUI/OCSStore.sql b/UI/MainUI/OCSStore.sql new file mode 100644 index 000000000..5688ece83 --- /dev/null +++ b/UI/MainUI/OCSStore.sql @@ -0,0 +1,15 @@ +-- +-- (C) 2004-2005 SKYRIX Software AG +-- (C) 2006-2007 Inverse inc. +-- + +CREATE TABLE @{tableName} ( + c_folder_id INT NOT NULL, + c_name VARCHAR (255), + c_content TEXT NOT NULL, + c_creationdate INT NOT NULL, + c_lastmodified INT NOT NULL, + c_version INT NOT NULL, + c_deleted INT NULL, + CONSTRAINT @{tableName}_pkey PRIMARY KEY (c_folder_id, c_name) +);