2014-05-14 03:06:59 +02:00
|
|
|
/* SOGoCacheGCSObject.m - this file is part of SOGo
|
2012-06-29 19:59:38 +02:00
|
|
|
*
|
2016-03-10 22:34:06 +01:00
|
|
|
* Copyright (C) 2012-2016 Inverse inc
|
2012-06-29 19:59:38 +02:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This file is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import <GDLAccess/EOAdaptor.h>
|
|
|
|
#import <GDLAccess/EOAdaptorContext.h>
|
|
|
|
#import <GDLAccess/EOAttribute.h>
|
|
|
|
#import <GDLContentStore/GCSChannelManager.h>
|
|
|
|
#import <NGExtensions/NGBase64Coding.h>
|
|
|
|
#import <NGExtensions/NSNull+misc.h>
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
2016-03-10 22:34:06 +01:00
|
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
2014-12-04 17:27:10 +01:00
|
|
|
#import <SOGo/SOGoCache.h>
|
2016-03-10 22:34:06 +01:00
|
|
|
#import <SOGo/SOGoUser.h>
|
2012-06-29 19:59:38 +02:00
|
|
|
#import <SOGo/NSObject+Utilities.h>
|
|
|
|
#import <SOGo/NSString+Utilities.h>
|
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
#import "GCSSpecialQueries+SOGoCacheObject.h"
|
|
|
|
#import "SOGoCacheGCSFolder.h"
|
2013-02-18 22:28:06 +01:00
|
|
|
#import "BSONCodec.h"
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
static EOAttribute *textColumn = nil;
|
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
@implementation SOGoCacheGCSObject
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
NSDictionary *description;
|
|
|
|
|
|
|
|
if (!textColumn)
|
|
|
|
{
|
|
|
|
/* TODO: this is a hack for providing an EOAttribute definition that is
|
|
|
|
compatible with all the backends that we support. We should make use
|
|
|
|
of EOModel instead. */
|
|
|
|
description = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
@"c_textfield", @"columnName",
|
|
|
|
@"VARCHAR", @"externalType",
|
|
|
|
nil];
|
|
|
|
textColumn = [EOAttribute attributeFromPropertyList: description];
|
|
|
|
[textColumn retain];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
= (@"CREATE TABLE %@ ("
|
|
|
|
@" c_path VARCHAR(255) PRIMARY KEY,"
|
2014-05-13 23:54:02 +02:00
|
|
|
@" c_parent_path VARCHAR(255),"
|
|
|
|
@" c_type SMALLINT NOT NULL,"
|
2012-06-29 19:59:38 +02:00
|
|
|
@" c_creationdate INT4 NOT NULL,"
|
|
|
|
@" c_lastmodified INT4 NOT NULL,"
|
|
|
|
@" c_version INT4 NOT NULL DEFAULT 0,"
|
|
|
|
@" c_deleted SMALLINT NOT NULL DEFAULT 0,"
|
2014-05-13 23:54:02 +02:00
|
|
|
@" c_content TEXT)");
|
2012-06-29 19:59:38 +02:00
|
|
|
*/
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
tableUrl = nil;
|
|
|
|
initialized = NO;
|
2015-10-31 07:10:03 +01:00
|
|
|
objectType = (SOGoCacheObjectType) -1;
|
2012-06-29 19:59:38 +02:00
|
|
|
deleted = NO;
|
|
|
|
version = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2014-12-04 17:27:10 +01:00
|
|
|
//NSLog(@"SOGoCacheGCSObject: -dealloc for name: %@", nameInContainer);
|
2012-06-29 19:59:38 +02:00
|
|
|
[tableUrl release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2014-12-04 17:27:10 +01:00
|
|
|
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer
|
2015-10-14 15:21:32 +02:00
|
|
|
{
|
|
|
|
return [self objectWithName: key
|
|
|
|
inContainer: theContainer
|
|
|
|
useCache: YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache
|
2014-12-04 17:27:10 +01:00
|
|
|
{
|
|
|
|
SOGoCache *cache;
|
|
|
|
id o;
|
|
|
|
|
|
|
|
cache = [SOGoCache sharedCache];
|
2015-10-14 15:21:32 +02:00
|
|
|
|
|
|
|
if (!useCache)
|
|
|
|
[cache unregisterObjectWithName: key inContainer: theContainer];
|
|
|
|
|
2014-12-04 17:27:10 +01:00
|
|
|
o = [cache objectNamed: key inContainer: theContainer];
|
|
|
|
|
|
|
|
if (!o)
|
|
|
|
{
|
|
|
|
o = [super objectWithName: key inContainer: theContainer];
|
|
|
|
//NSLog(@"Caching object with key: %@", key);
|
|
|
|
[cache registerObject: o withName: key inContainer: theContainer];
|
|
|
|
}
|
|
|
|
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
- (void) setTableUrl: (NSURL *) newTableUrl
|
|
|
|
{
|
|
|
|
ASSIGN (tableUrl, newTableUrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) tableUrl
|
|
|
|
{
|
2016-03-10 22:34:06 +01:00
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = [[NSUserDefaults standardUserDefaults] stringForKey: @"OCSCacheFolderURL"];
|
|
|
|
|
|
|
|
if (s)
|
|
|
|
{
|
|
|
|
tableUrl = [NSURL URLWithString: s];
|
|
|
|
[tableUrl retain];
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
if (!tableUrl)
|
|
|
|
{
|
|
|
|
tableUrl = [container tableUrl];
|
|
|
|
[tableUrl retain];
|
|
|
|
if (!tableUrl)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"table url is not set for object '%@'", self];
|
|
|
|
}
|
|
|
|
|
|
|
|
return tableUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) tableName
|
|
|
|
{
|
|
|
|
NSArray *parts;
|
|
|
|
|
|
|
|
[self tableUrl];
|
|
|
|
parts = [[tableUrl path] componentsSeparatedByString: @"/"];
|
|
|
|
|
|
|
|
return [parts lastObject];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setupFromRecord: (NSDictionary *) record
|
|
|
|
{
|
|
|
|
NSInteger intValue;
|
2014-05-14 16:51:16 +02:00
|
|
|
NSString *propsValue;
|
2012-06-29 19:59:38 +02:00
|
|
|
NSDictionary *newValues;
|
|
|
|
|
|
|
|
objectType = [[record objectForKey: @"c_type"] intValue];
|
|
|
|
intValue = [[record objectForKey: @"c_creationdate"] intValue];
|
|
|
|
ASSIGN (creationDate,
|
|
|
|
[NSCalendarDate
|
|
|
|
dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]);
|
|
|
|
intValue = [[record objectForKey: @"c_lastmodified"] intValue];
|
|
|
|
ASSIGN (lastModified,
|
|
|
|
[NSCalendarDate
|
|
|
|
dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]);
|
|
|
|
deleted = ([[record objectForKey: @"c_deleted"] intValue] > 0);
|
|
|
|
version = [[record objectForKey: @"c_version"] intValue];
|
|
|
|
propsValue = [record objectForKey: @"c_content"];
|
|
|
|
if ([propsValue isNotNull])
|
|
|
|
{
|
2013-02-18 22:28:06 +01:00
|
|
|
newValues = [[propsValue dataByDecodingBase64] BSONValue];
|
2012-06-29 19:59:38 +02:00
|
|
|
[properties addEntriesFromDictionary: newValues];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
[properties removeAllObjects];
|
|
|
|
|
|
|
|
initialized = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
- (NSMutableString *) path
|
|
|
|
{
|
|
|
|
NSMutableString *path;
|
|
|
|
|
|
|
|
if (container)
|
|
|
|
path = [container pathForChild: nameInContainer];
|
|
|
|
else
|
|
|
|
path = [NSMutableString stringWithFormat: @"/%@", nameInContainer];
|
|
|
|
|
|
|
|
if ([path rangeOfString: @"//"].location != NSNotFound)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"object path has not been properly set for"
|
|
|
|
" folder '%@' (%@)",
|
|
|
|
self, path];
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2014-05-14 00:03:37 +02:00
|
|
|
- (void) setObjectType: (SOGoCacheObjectType) newObjectType
|
2012-06-29 19:59:38 +02:00
|
|
|
{
|
|
|
|
objectType = newObjectType;
|
|
|
|
}
|
|
|
|
|
2014-05-14 00:03:37 +02:00
|
|
|
- (SOGoCacheObjectType) objectType /* message, fai, folder */
|
2012-06-29 19:59:38 +02:00
|
|
|
{
|
|
|
|
return objectType;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSCalendarDate *) creationDate
|
|
|
|
{
|
|
|
|
if (!initialized)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"record has not been initialized: %@", self];
|
|
|
|
|
|
|
|
return creationDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSCalendarDate *) lastModified
|
|
|
|
{
|
|
|
|
if (!initialized)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"record has not been initialized: %@", self];
|
|
|
|
|
|
|
|
return lastModified;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) deleted
|
|
|
|
{
|
|
|
|
return deleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* actions */
|
2012-08-15 21:00:38 +02:00
|
|
|
- (void) setNameInContainer: (NSString *) newNameInContainer
|
|
|
|
{
|
|
|
|
NSMutableString *sql;
|
|
|
|
NSString *oldPath, *newPath;
|
|
|
|
|
|
|
|
if (nameInContainer)
|
|
|
|
oldPath = [self path];
|
|
|
|
|
|
|
|
[super setNameInContainer: newNameInContainer];
|
|
|
|
|
|
|
|
if (nameInContainer)
|
|
|
|
{
|
|
|
|
newPath = [self path];
|
|
|
|
|
|
|
|
sql = [NSMutableString stringWithFormat: @"UPDATE %@"
|
|
|
|
@" SET c_path = '%@'",
|
|
|
|
[self tableName],
|
|
|
|
newPath];
|
|
|
|
[sql appendFormat: @" WHERE c_path = '%@'", oldPath];
|
2016-03-10 22:34:06 +01:00
|
|
|
|
|
|
|
if ([GCSFolderManager singleStoreMode])
|
|
|
|
[sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]];
|
|
|
|
|
2012-08-15 21:00:38 +02:00
|
|
|
[self performBatchSQLQueries: [NSArray arrayWithObject: sql]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-13 19:48:18 +02:00
|
|
|
- (void) changePathTo: (NSString *) newPath
|
|
|
|
{
|
|
|
|
NSMutableString *sql;
|
|
|
|
NSString *oldPath, *newParentPath;
|
|
|
|
NSRange slashRange;
|
|
|
|
|
|
|
|
oldPath = [self path];
|
|
|
|
|
|
|
|
slashRange = [newPath rangeOfString: @"/"
|
|
|
|
options: NSBackwardsSearch];
|
|
|
|
if (slashRange.location != NSNotFound)
|
|
|
|
newParentPath = [newPath substringToIndex: slashRange.location];
|
|
|
|
else
|
|
|
|
newParentPath = NULL;
|
|
|
|
|
|
|
|
sql = [NSMutableString stringWithFormat: @"UPDATE %@"
|
2014-10-29 19:20:03 +01:00
|
|
|
@" SET c_path = '/%@'",
|
2012-08-13 19:48:18 +02:00
|
|
|
[self tableName],
|
|
|
|
newPath];
|
|
|
|
if (newParentPath)
|
|
|
|
[sql appendFormat: @", c_parent_path = '%@'", newParentPath];
|
|
|
|
else
|
|
|
|
[sql appendString: @", c_parent_path = NULL"];
|
|
|
|
[sql appendFormat: @" WHERE c_path = '%@'", oldPath];
|
2016-03-10 22:34:06 +01:00
|
|
|
|
|
|
|
if ([GCSFolderManager singleStoreMode])
|
|
|
|
[sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]];
|
|
|
|
|
2012-08-13 19:48:18 +02:00
|
|
|
[self performBatchSQLQueries: [NSArray arrayWithObject: sql]];
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
- (EOAdaptor *) tableChannelAdaptor
|
|
|
|
{
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
EOAdaptor *adaptor;
|
|
|
|
EOAdaptorChannel *channel;
|
|
|
|
|
|
|
|
cm = [GCSChannelManager defaultChannelManager];
|
|
|
|
channel = [cm acquireOpenChannelForURL: [self tableUrl]];
|
|
|
|
adaptor = [[channel adaptorContext] adaptor];
|
|
|
|
[cm releaseChannel: channel];
|
|
|
|
|
|
|
|
return adaptor;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) performSQLQuery: (NSString *) sql
|
|
|
|
{
|
|
|
|
NSMutableArray *records;
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
EOAdaptorChannel *channel;
|
|
|
|
NSException *error;
|
|
|
|
NSArray *attrs;
|
|
|
|
NSDictionary *record;
|
|
|
|
|
|
|
|
cm = [GCSChannelManager defaultChannelManager];
|
|
|
|
channel = [cm acquireOpenChannelForURL: [self tableUrl]];
|
|
|
|
|
|
|
|
error = [channel evaluateExpressionX: sql];
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
records = nil;
|
|
|
|
[self logWithFormat:
|
|
|
|
@"an exception occurred when executing query '%@'",
|
|
|
|
sql];
|
|
|
|
[self logWithFormat: @"exception is '%@'", error];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
records = [NSMutableArray arrayWithCapacity: 256];
|
|
|
|
attrs = [channel describeResults: NO];
|
|
|
|
while ((record = [channel fetchAttributes: attrs withZone: NULL]))
|
|
|
|
[records addObject: record];
|
|
|
|
}
|
|
|
|
[cm releaseChannel: channel];
|
|
|
|
|
|
|
|
return records;
|
|
|
|
}
|
|
|
|
|
2012-08-13 19:48:18 +02:00
|
|
|
- (BOOL) performBatchSQLQueries: (NSArray *) queries
|
|
|
|
{
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
EOAdaptorChannel *channel;
|
|
|
|
EOAdaptorContext *dbContext;
|
|
|
|
NSException *error;
|
|
|
|
NSUInteger count, max;
|
|
|
|
NSString *sql;
|
|
|
|
|
|
|
|
cm = [GCSChannelManager defaultChannelManager];
|
|
|
|
channel = [cm acquireOpenChannelForURL: [self tableUrl]];
|
|
|
|
dbContext = [channel adaptorContext];
|
|
|
|
|
|
|
|
[dbContext beginTransaction];
|
|
|
|
|
|
|
|
error = nil;
|
|
|
|
|
|
|
|
max = [queries count];
|
|
|
|
for (count = 0; error == nil && count < max; count++)
|
|
|
|
{
|
|
|
|
sql = [queries objectAtIndex: count];
|
|
|
|
error = [channel evaluateExpressionX: sql];
|
|
|
|
if (error)
|
|
|
|
[dbContext rollbackTransaction];
|
|
|
|
}
|
|
|
|
if (!error)
|
|
|
|
[dbContext commitTransaction];
|
|
|
|
[cm releaseChannel: channel];
|
|
|
|
|
|
|
|
return (error == nil);
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
- (NSDictionary *) lookupRecord: (NSString *) path
|
|
|
|
newerThanVersion: (NSInteger) startVersion
|
|
|
|
{
|
|
|
|
NSDictionary *record;
|
|
|
|
NSArray *records;
|
|
|
|
NSString *tableName, *pathValue;
|
|
|
|
NSMutableString *sql;
|
|
|
|
EOAdaptor *adaptor;
|
|
|
|
|
|
|
|
if ([path hasSuffix: @"/"])
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"path ends with a slash: %@", path];
|
|
|
|
|
|
|
|
tableName = [self tableName];
|
|
|
|
adaptor = [self tableChannelAdaptor];
|
|
|
|
pathValue = [adaptor formatValue: path
|
|
|
|
forAttribute: textColumn];
|
|
|
|
|
|
|
|
/* query */
|
|
|
|
sql = [NSMutableString stringWithFormat:
|
|
|
|
@"SELECT * FROM %@ WHERE c_path = %@",
|
|
|
|
tableName, pathValue];
|
2016-03-10 22:34:06 +01:00
|
|
|
|
|
|
|
if ([GCSFolderManager singleStoreMode])
|
|
|
|
[sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]];
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
if (startVersion > -1)
|
2015-10-31 07:10:03 +01:00
|
|
|
[sql appendFormat: @" AND c_version > %d", (int)startVersion];
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
/* execution */
|
|
|
|
records = [self performSQLQuery: sql];
|
|
|
|
if ([records count] > 0)
|
|
|
|
record = [records objectAtIndex: 0];
|
|
|
|
else
|
|
|
|
record = nil;
|
|
|
|
|
|
|
|
return record;
|
|
|
|
}
|
|
|
|
|
2014-10-29 19:20:03 +01:00
|
|
|
- (NSArray *) cacheEntriesForDeviceId: (NSString *) deviceId
|
|
|
|
newerThanVersion: (NSInteger) startVersion
|
2014-05-27 20:44:57 +02:00
|
|
|
{
|
|
|
|
NSMutableArray *recordsOut;
|
|
|
|
NSArray *records;
|
|
|
|
NSString *tableName, *pathValue;
|
|
|
|
NSMutableString *sql;
|
|
|
|
EOAdaptor *adaptor;
|
|
|
|
NSUInteger count, max;
|
|
|
|
|
|
|
|
if ([deviceId hasSuffix: @"/"])
|
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
|
|
|
format: @"path ends with a slash: %@", deviceId];
|
|
|
|
|
|
|
|
tableName = [self tableName];
|
|
|
|
adaptor = [self tableChannelAdaptor];
|
|
|
|
|
|
|
|
/* query */
|
|
|
|
sql = [NSMutableString stringWithFormat:
|
2014-10-29 19:20:03 +01:00
|
|
|
@"SELECT * FROM %@ WHERE c_type = %d AND c_deleted <> 1", tableName, objectType];
|
|
|
|
|
2016-03-10 22:34:06 +01:00
|
|
|
if ([GCSFolderManager singleStoreMode])
|
|
|
|
[sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]];
|
|
|
|
|
2014-05-27 20:44:57 +02:00
|
|
|
if (startVersion > -1)
|
2015-10-31 07:10:03 +01:00
|
|
|
[sql appendFormat: @" AND c_version > %d", (int)startVersion];
|
2014-05-27 20:44:57 +02:00
|
|
|
|
2014-10-29 19:20:03 +01:00
|
|
|
if (deviceId) {
|
2015-11-06 14:56:23 +01:00
|
|
|
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@%", deviceId]
|
2014-10-29 19:20:03 +01:00
|
|
|
forAttribute: textColumn];
|
|
|
|
[sql appendFormat: @" AND c_path like %@", pathValue];
|
|
|
|
}
|
|
|
|
|
2014-05-27 20:44:57 +02:00
|
|
|
/* execution */
|
|
|
|
records = [self performSQLQuery: sql];
|
|
|
|
|
|
|
|
max = [records count];
|
|
|
|
recordsOut = [[NSMutableArray alloc] init];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
[recordsOut addObject: [[records objectAtIndex: count] objectForKey: @"c_path"]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return recordsOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
- (void) reloadIfNeeded
|
|
|
|
{
|
|
|
|
/* if object is uninitialized: reload without condition, otherwise, load if
|
|
|
|
c_version > :version */
|
|
|
|
NSDictionary *record;
|
|
|
|
|
|
|
|
if (initialized)
|
|
|
|
{
|
|
|
|
if (!isNew)
|
|
|
|
{
|
|
|
|
record = [self lookupRecord: [self path]
|
|
|
|
newerThanVersion: version];
|
|
|
|
if (record)
|
|
|
|
[self setupFromRecord: record];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
record = [self lookupRecord: [self path]
|
|
|
|
newerThanVersion: -1];
|
|
|
|
if (record)
|
|
|
|
{
|
|
|
|
[self setupFromRecord: record];
|
|
|
|
isNew = NO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
isNew = YES;
|
|
|
|
initialized = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSException *) delete
|
|
|
|
{
|
|
|
|
deleted = YES;
|
|
|
|
[properties removeAllObjects];
|
|
|
|
[self save];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2014-07-24 14:39:41 +02:00
|
|
|
- (NSException *) destroy
|
|
|
|
{
|
2016-03-10 22:34:06 +01:00
|
|
|
NSString *tableName, *pathValue;
|
2014-07-24 14:39:41 +02:00
|
|
|
EOAdaptorChannel *channel;
|
2016-03-10 22:34:06 +01:00
|
|
|
NSMutableString *sql;
|
2014-07-24 14:39:41 +02:00
|
|
|
GCSChannelManager *cm;
|
|
|
|
NSException *result;
|
|
|
|
EOAdaptor *adaptor;
|
|
|
|
|
|
|
|
cm = [GCSChannelManager defaultChannelManager];
|
|
|
|
channel = [cm acquireOpenChannelForURL: [self tableUrl]];
|
|
|
|
tableName = [self tableName];
|
|
|
|
|
|
|
|
adaptor = [[channel adaptorContext] adaptor];
|
|
|
|
pathValue = [adaptor formatValue: [self path]
|
|
|
|
forAttribute: textColumn];
|
|
|
|
result = nil;
|
|
|
|
|
2016-03-10 22:34:06 +01:00
|
|
|
sql = [NSMutableString stringWithFormat:
|
|
|
|
(@"DELETE FROM %@"
|
|
|
|
@" WHERE c_path = %@"),
|
|
|
|
tableName,
|
|
|
|
pathValue];
|
|
|
|
|
|
|
|
if ([GCSFolderManager singleStoreMode])
|
|
|
|
[sql appendFormat: @" AND c_uid = '%@'", [[context activeUser] login]];
|
2014-07-24 14:39:41 +02:00
|
|
|
|
|
|
|
result = [channel evaluateExpressionX: sql];
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
[self errorWithFormat: @"could not delete record %@"
|
|
|
|
@" in %@: %@", pathValue, tableName, result];
|
|
|
|
|
|
|
|
[cm releaseChannel: channel];
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
- (void) save
|
|
|
|
{
|
2016-03-10 22:34:06 +01:00
|
|
|
NSMutableString *sql;
|
2012-06-29 19:59:38 +02:00
|
|
|
NSData *content;
|
|
|
|
NSCalendarDate *now;
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
EOAdaptor *adaptor;
|
|
|
|
EOAdaptorChannel *channel;
|
|
|
|
NSInteger creationDateValue, lastModifiedValue, deletedValue;
|
2016-03-10 22:34:06 +01:00
|
|
|
NSString *tableName, *loginValue, *pathValue, *parentPathValue, *propsValue;
|
2012-06-29 19:59:38 +02:00
|
|
|
NSException *result;
|
|
|
|
|
|
|
|
if (!initialized)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"record has not been initialized: %@", self];
|
|
|
|
|
|
|
|
cm = [GCSChannelManager defaultChannelManager];
|
|
|
|
|
|
|
|
channel = [cm acquireOpenChannelForURL: [self tableUrl]];
|
|
|
|
|
|
|
|
tableName = [self tableName];
|
|
|
|
|
|
|
|
now = [NSCalendarDate date];
|
|
|
|
ASSIGN (lastModified, now);
|
|
|
|
|
|
|
|
adaptor = [[channel adaptorContext] adaptor];
|
|
|
|
pathValue = [adaptor formatValue: [self path]
|
|
|
|
forAttribute: textColumn];
|
2016-03-10 22:34:06 +01:00
|
|
|
loginValue = [adaptor formatValue: [[context activeUser] login]
|
|
|
|
forAttribute: textColumn];
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
|
|
|
|
|
2015-10-31 07:10:03 +01:00
|
|
|
if (objectType == (SOGoCacheObjectType) -1)
|
2014-05-27 20:44:57 +02:00
|
|
|
[NSException raise: @"SOGoCacheIOException"
|
2012-06-29 19:59:38 +02:00
|
|
|
format: @"object type has not been set for object '%@'",
|
|
|
|
self];
|
|
|
|
|
|
|
|
if ([properties count] > 0)
|
|
|
|
{
|
2013-02-18 22:28:06 +01:00
|
|
|
content = [properties BSONRepresentation];
|
2012-06-29 19:59:38 +02:00
|
|
|
propsValue = [adaptor formatValue: [content stringByEncodingBase64]
|
|
|
|
forAttribute: textColumn];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
propsValue = @"NULL";
|
|
|
|
|
|
|
|
if (isNew)
|
|
|
|
{
|
|
|
|
ASSIGN (creationDate, now);
|
|
|
|
creationDateValue = (NSInteger) [creationDate timeIntervalSince1970];
|
2012-08-13 16:35:00 +02:00
|
|
|
parentPathValue = [adaptor formatValue: [container path]
|
|
|
|
forAttribute: textColumn];
|
|
|
|
if (!parentPathValue)
|
|
|
|
parentPathValue = @"NULL";
|
2016-03-10 22:34:06 +01:00
|
|
|
|
|
|
|
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];
|
2012-06-29 19:59:38 +02:00
|
|
|
isNew = NO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
version++;
|
|
|
|
deletedValue = (deleted ? 1 : 0);
|
2016-03-10 22:34:06 +01:00
|
|
|
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];
|
2012-06-29 19:59:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
result = [channel evaluateExpressionX: sql];
|
|
|
|
if (result)
|
|
|
|
[self errorWithFormat: @"could not insert/update record for record %@"
|
|
|
|
@" in %@: %@", pathValue, tableName, result];
|
|
|
|
|
|
|
|
[cm releaseChannel: channel];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|