#include <GDLContentStore/GCSChannelManager.h>
#include <GDLContentStore/NSURL+GCS.h>
#include <GDLAccess/EOAdaptorChannel.h>
+#include <GDLAccess/EOAdaptorContext.h>
+#include <GDLAccess/EOAttribute.h>
#include "common.h"
@implementation AgenorUserDefaults
}
- (void)dealloc {
- [self->lastFetch release];
- [self->parent release];
- [self->url release];
- [self->uid release];
+ [self->attributes release];
+ [self->lastFetch release];
+ [self->parent release];
+ [self->url release];
+ [self->uid release];
[super dealloc];
}
/* operation */
+- (void)_loadAttributes:(NSArray *)_attrs {
+ NSMutableArray *fields;
+ NSMutableDictionary *attrmap;
+ unsigned i, count;
+
+ fields = [[NSMutableArray alloc] initWithCapacity:16];
+ attrmap = [[NSMutableDictionary alloc] initWithCapacity:16];
+ for (i = 0, count = [_attrs count]; i < count; i++) {
+ EOAttribute *attr;
+ NSString *name;
+
+ attr = [_attrs objectAtIndex:i];
+ name = [attr valueForKey:@"name"];
+ [attrmap setObject:attr forKey:name];
+
+ if (![name isEqual:uidColumnName])
+ [fields addObject:name];
+ }
+
+ ASSIGNCOPY(self->fieldNames, fields);
+ ASSIGNCOPY(self->attributes, attrmap);
+ [attrmap release];
+ [fields release];
+}
+
- (BOOL)primaryFetchProfile {
GCSChannelManager *cm;
EOAdaptorChannel *channel;
NSException *ex;
NSString *sql;
NSArray *attrs;
- NSMutableArray *fields;
cm = [GCSChannelManager defaultChannelManager];
if ((channel = [cm acquireOpenChannelForURL:[self tableURL]]) == nil) {
/* fetch schema */
attrs = [channel describeResults:NO /* don't beautify */];
-
- fields = [[attrs valueForKey:@"name"] mutableCopy];
- [fields removeObject:uidColumnName];
- ASSIGNCOPY(self->fieldNames, fields);
- [fields release]; fields =nil;
+ [self _loadAttributes:attrs];
/* fetch values */
row = [channel fetchAttributes:attrs withZone:NULL];
[channel cancelFetch];
-
+
/* remember values */
[self->values release]; self->values = nil;
return YES;
}
+- (NSString *)generateSQLForUpdate {
+ NSMutableString *sql;
+ unsigned i, count;
+
+ if ([self->values count] == 0)
+ return nil;
+
+ sql = [NSMutableString stringWithCapacity:2048];
+
+ [sql appendString:@"UPDATE "];
+ [sql appendString:[[self tableURL] gcsTableName]];
+ [sql appendString:@" SET "];
+
+ for (i = 0, count = [self->fieldNames count]; i < count; i++) {
+ EOAttribute *attr;
+ NSString *name;
+ id value;
+
+ name = [self->fieldNames objectAtIndex:i];
+ value = [self->values objectForKey:name];
+ attr = [self->attributes objectForKey:name];
+
+ if (i != 0) [sql appendString:@", "];
+ [sql appendString:[attr columnName]];
+
+ if ([value isNotNull]) {
+ /* a rather limited set of supported value types */
+
+ if ([[attr externalType] hasPrefix:@"int"])
+ [sql appendFormat:@" = %i", [value intValue]];
+ else {
+ // TODO: any other escaping?! move to adaptor?
+ value = [value stringValue];
+ value = [value stringByReplacingString:@"'" withString:@"''"];
+ [sql appendString:@" = '"];
+ [sql appendString:value];
+ [sql appendString:@"'"];
+ }
+ }
+ else
+ [sql appendString:@" = NULL"];
+ }
+
+ [sql appendString:@" WHERE "];
+ [sql appendString:uidColumnName];
+ [sql appendString:@" = '"];
+ [sql appendString:[self uid]];
+ [sql appendString:@"'"];
+ return sql;
+}
+
+- (BOOL)primaryStoreProfile {
+ GCSChannelManager *cm;
+ EOAdaptorChannel *channel;
+ NSException *ex;
+ NSString *sql;
+
+ cm = [GCSChannelManager defaultChannelManager];
+ if ((channel = [cm acquireOpenChannelForURL:[self tableURL]]) == nil) {
+ [self errorWithFormat:@"failed to acquire channel for URL: %@",
+ [self tableURL]];
+ return NO;
+ }
+
+ /* run SQL */
+
+ sql = [self generateSQLForUpdate];
+ if ((ex = [channel evaluateExpressionX:sql]) != nil) {
+ [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
+ [cm releaseChannel:channel];
+ return NO;
+ }
+
+ /* commit */
+
+ ex = nil;
+ if ([[channel adaptorContext] hasOpenTransaction])
+ ex = [channel evaluateExpressionX:@"COMMIT TRANSACTION"];
+
+ [cm releaseChannel:channel];
+
+ if (ex != nil) {
+ [self errorWithFormat:@"could not commit transaction for update: %@", ex];
+ return NO;
+ }
+
+ self->defFlags.modified = 0;
+ return YES;
+}
+
+
- (BOOL)fetchProfile {
if (self->values != nil)
return YES;
- (BOOL)synchronize {
if (!self->defFlags.modified) /* was not modified */
return YES;
+
+ /* ensure fetched data (more or less guaranteed by modified!=0) */
+ if (![self fetchProfile])
+ return NO;
+
+ /* store */
+ if (![self primaryStoreProfile]) {
+ [self primaryFetchProfile];
+ return NO;
+ }
+
+ /* refetch */
+ return [self primaryFetchProfile];
+}
- [self logWithFormat:@"TODO: sync!"];
- return NO;
+- (void)flush {
+ [self->values release]; self->values = nil;
+ [self->fieldNames release]; self->fieldNames = nil;
+ [self->attributes release]; self->attributes = nil;
+ [self->lastFetch release]; self->lastFetch = nil;
+ self->defFlags.modified = 0;
}
/* typed accessors */