2 Copyright (C) 2004-2007 SKYRIX Software AG
3 Copyright (C) 2007 Helge Hess
5 This file is part of OpenGroupware.org.
7 OGo is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with OGo; see the file COPYING. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "GCSFolder.h"
24 #include "GCSFolderManager.h"
25 #include "GCSFolderType.h"
26 #include "GCSChannelManager.h"
27 #include "GCSFieldExtractor.h"
28 #include "NSURL+GCS.h"
29 #include "EOAdaptorChannel+GCS.h"
30 #include "EOQualifier+GCS.h"
31 #include "GCSStringFormatter.h"
34 @implementation GCSFolder
36 static BOOL debugOn = NO;
37 static BOOL doLogStore = NO;
39 static Class NSStringClass = Nil;
40 static Class NSNumberClass = Nil;
41 static Class NSCalendarDateClass = Nil;
43 static GCSStringFormatter *stringFormatter = nil;
46 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48 debugOn = [ud boolForKey:@"GCSFolderDebugEnabled"];
49 doLogStore = [ud boolForKey:@"GCSFolderStoreDebugEnabled"];
51 NSStringClass = [NSString class];
52 NSNumberClass = [NSNumber class];
53 NSCalendarDateClass = [NSCalendarDate class];
55 stringFormatter = [GCSStringFormatter sharedFormatter];
58 - (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId
59 folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype
60 location:(NSURL *)_loc quickLocation:(NSURL *)_qloc
61 aclLocation:(NSURL *)_aloc
62 folderManager:(GCSFolderManager *)_fm
64 if (![_loc isNotNull]) {
65 [self errorWithFormat:@"missing quicktable parameter!"];
70 if ((self = [super init])) {
71 self->folderManager = [_fm retain];
72 self->folderInfo = [_ftype retain];
74 self->folderId = [_folderId copy];
75 self->folderName = [[_path lastPathComponent] copy];
76 self->path = [_path copy];
77 self->location = [_loc retain];
78 self->quickLocation = _qloc ? [_qloc retain] : [_loc retain];
79 self->aclLocation = [_aloc retain];
80 self->folderTypeName = [_ftname copy];
82 self->ofFlags.requiresFolderSelect = 0;
83 self->ofFlags.sameTableForQuick =
84 [self->location isEqualTo:self->quickLocation] ? 1 : 0;
89 return [self initWithPath:nil primaryKey:nil
90 folderTypeName:nil folderType:nil
91 location:nil quickLocation:nil
95 - (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId
96 folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype
97 location:(NSURL *)_loc quickLocation:(NSURL *)_qloc
98 folderManager:(GCSFolderManager *)_fm
100 return [self initWithPath:_path primaryKey:_folderId folderTypeName:_ftname
101 folderType:_ftype location:_loc quickLocation:_qloc
107 [self->folderManager release];
108 [self->folderInfo release];
109 [self->folderId release];
110 [self->folderName release];
111 [self->path release];
112 [self->location release];
113 [self->quickLocation release];
114 [self->aclLocation release];
115 [self->folderTypeName release];
121 - (NSNumber *)folderId {
122 return self->folderId;
125 - (NSString *)folderName {
126 return self->folderName;
132 - (NSURL *)location {
133 return self->location;
135 - (NSURL *)quickLocation {
136 return self->quickLocation;
138 - (NSURL *)aclLocation {
139 return self->aclLocation;
142 - (NSString *)folderTypeName {
143 return self->folderTypeName;
146 - (GCSFolderManager *)folderManager {
147 return self->folderManager;
149 - (GCSChannelManager *)channelManager {
150 return [[self folderManager] channelManager];
153 - (NSString *)storeTableName {
154 return [[self location] gcsTableName];
156 - (NSString *)quickTableName {
157 return [[self quickLocation] gcsTableName];
159 - (NSString *)aclTableName {
160 return [[self aclLocation] gcsTableName];
163 - (BOOL)isQuickInfoStoredInContentTable {
164 return self->ofFlags.sameTableForQuick ? YES : NO;
169 - (EOAdaptorChannel *)acquireStoreChannel {
170 return [[self channelManager] acquireOpenChannelForURL:[self location]];
172 - (EOAdaptorChannel *)acquireQuickChannel {
173 return [[self channelManager] acquireOpenChannelForURL:[self quickLocation]];
175 - (EOAdaptorChannel *)acquireAclChannel {
176 return [[self channelManager] acquireOpenChannelForURL:[self aclLocation]];
179 - (void)releaseChannel:(EOAdaptorChannel *)_channel {
180 [[self channelManager] releaseChannel:_channel];
181 if (debugOn) [self debugWithFormat:@"released channel: %@", _channel];
184 - (BOOL)canConnectStore {
185 return [[self channelManager] canConnect:[self location]];
187 - (BOOL)canConnectQuick {
188 return [[self channelManager] canConnect:[self quickLocation]];
190 - (BOOL)canConnectAcl {
191 return [[self channelManager] canConnect:[self quickLocation]];
196 - (NSException *)errorVersionMismatchBetweenStoredVersion:(unsigned int)_store
197 andExpectedVersion:(unsigned int)_base
201 ui = [NSDictionary dictionaryWithObjectsAndKeys:
202 [NSNumber numberWithUnsignedInt:_base],
203 @"GCSExpectedVersion",
204 [NSNumber numberWithUnsignedInt:_store],
209 return [NSException exceptionWithName:@"GCSVersionMismatch"
210 reason:@"Transaction conflict during a GCS modification."
214 - (NSException *)errorExtractorReturnedNoQuickRow:(id)_extractor
215 forContent:(NSString *)_content
219 ui = [NSDictionary dictionaryWithObjectsAndKeys:
221 _extractor, @"GCSExtractor",
222 _content, @"GCSContent",
224 return [NSException exceptionWithName:@"GCSExtractFailed"
225 reason:@"Quickfield extractor did not return a result!"
231 - (NSArray *)subFolderNames {
232 return [[self folderManager] listSubFoldersAtPath:[self path]
235 - (NSArray *)allSubFolderNames {
236 return [[self folderManager] listSubFoldersAtPath:[self path]
240 - (id)_fetchValueOfColumn:(NSString *)_col inContentWithName:(NSString *)_name{
241 EOAdaptorChannel *channel;
248 if ((channel = [self acquireStoreChannel]) == nil) {
249 [self errorWithFormat:@"could not open storage channel!"];
256 sql = [sql stringByAppendingString:_col];
257 sql = [sql stringByAppendingString:@" FROM "];
258 sql = [sql stringByAppendingString:[self storeTableName]];
259 sql = [sql stringByAppendingString:@" WHERE \"c_name\" = '"];
260 sql = [sql stringByAppendingString:_name];
261 sql = [sql stringByAppendingString:@"'"];
265 if ((error = [channel evaluateExpressionX:sql]) != nil) {
266 [self errorWithFormat:@"%s: cannot execute SQL '%@': %@",
267 __PRETTY_FUNCTION__, sql, error];
268 [self releaseChannel:channel];
275 attrs = [channel describeResults:NO /* do not beautify names */];
276 if ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) {
277 result = [[[row objectForKey:_col] copy] autorelease];
278 if (![result isNotNull]) result = nil;
279 [channel cancelFetch];
282 /* release and return result */
284 [self releaseChannel:channel];
288 - (NSNumber *)versionOfContentWithName:(NSString *)_name {
289 return [self _fetchValueOfColumn:@"c_version" inContentWithName:_name];
292 - (NSString *)fetchContentWithName:(NSString *)_name {
293 return [self _fetchValueOfColumn:@"c_content" inContentWithName:_name];
296 - (NSDictionary *)fetchContentsOfAllFiles {
298 Note: try to avoid the use of this method! The key of the dictionary
299 will be filename, the value the content.
301 NSMutableDictionary *result;
302 EOAdaptorChannel *channel;
308 if ((channel = [self acquireStoreChannel]) == nil) {
309 [self errorWithFormat:@"%s: could not open storage channel!",
310 __PRETTY_FUNCTION__];
316 sql = @"SELECT \"c_name\", \"c_content\" FROM ";
317 sql = [sql stringByAppendingString:[self storeTableName]];
321 if ((error = [channel evaluateExpressionX:sql]) != nil) {
322 [self logWithFormat:@"ERROR(%s): cannot execute SQL '%@': %@",
323 __PRETTY_FUNCTION__, sql, error];
324 [self releaseChannel:channel];
330 result = [NSMutableDictionary dictionaryWithCapacity:128];
331 attrs = [channel describeResults:NO /* do not beautify names */];
332 while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) {
333 NSString *cName, *cContent;
335 cName = [row objectForKey:@"c_name"];
336 cContent = [row objectForKey:@"c_content"];
338 if (![cName isNotNull]) {
339 [self errorWithFormat:@"missing c_name in row: %@", row];
342 if (![cContent isNotNull]) {
343 [self errorWithFormat:@"missing c_content in row: %@", row];
347 [result setObject:cContent forKey:cName];
350 /* release and return result */
352 [self releaseChannel:channel];
356 /* writing content */
358 - (NSString *)_formatRowValue:(id)_value {
359 if (![_value isNotNull])
362 if ([_value isKindOfClass:NSStringClass])
363 return [stringFormatter stringByFormattingString:_value];
365 if ([_value isKindOfClass:NSNumberClass]) {
366 #if GNUSTEP_BASE_LIBRARY
367 _value = [_value stringValue];
368 return ([(NSString *)_value hasPrefix:@"Y"] ||
369 [(NSString *)_value hasPrefix:@"N"])
370 ? (id)([_value boolValue] ? @"1" : @"0")
373 return [_value stringValue];
376 if ([_value isKindOfClass:NSCalendarDateClass]) {
377 /* be smart ... convert to timestamp. Note: we loose precision. */
379 snprintf(buf, sizeof(buf), "%i", (int)[_value timeIntervalSince1970]);
380 return [NSString stringWithCString:buf];
383 [self errorWithFormat:@"cannot handle value class: %@", [_value class]];
387 - (NSString *)_generateInsertStatementForRow:(NSDictionary *)_row
388 tableName:(NSString *)_table
390 // TODO: move to NSDictionary category?
391 NSMutableString *sql;
395 if (_row == nil || _table == nil)
398 keys = [_row allKeys];
400 sql = [NSMutableString stringWithCapacity:512];
401 [sql appendString:@"INSERT INTO "];
402 [sql appendString:_table];
403 [sql appendString:@" ("];
405 for (i = 0, count = [keys count]; i < count; i++) {
406 if (i != 0) [sql appendString:@", "];
407 [sql appendString:[keys objectAtIndex:i]];
410 [sql appendString:@") VALUES ("];
412 for (i = 0, count = [keys count]; i < count; i++) {
415 if (i != 0) [sql appendString:@", "];
416 value = [_row objectForKey:[keys objectAtIndex:i]];
417 value = [self _formatRowValue:value];
418 [sql appendString:value];
421 [sql appendString:@")"];
425 - (NSString *)_generateUpdateStatementForRow:(NSDictionary *)_row
426 tableName:(NSString *)_table
427 whereColumn:(NSString *)_colname isEqualTo:(id)_value
428 andColumn:(NSString *)_colname2 isEqualTo:(id)_value2
430 // TODO: move to NSDictionary category?
431 NSMutableString *sql;
435 if (_row == nil || _table == nil)
438 keys = [_row allKeys];
440 sql = [NSMutableString stringWithCapacity:512];
441 [sql appendString:@"UPDATE "];
442 [sql appendString:_table];
444 [sql appendString:@" SET "];
445 for (i = 0, count = [keys count]; i < count; i++) {
448 value = [_row objectForKey:[keys objectAtIndex:i]];
449 value = [self _formatRowValue:value];
451 if (i != 0) [sql appendString:@", "];
452 [sql appendString:[keys objectAtIndex:i]];
453 [sql appendString:@" = "];
454 [sql appendString:value];
457 [sql appendString:@" WHERE "];
458 [sql appendString:_colname];
459 [sql appendString:@" = "];
460 [sql appendString:[self _formatRowValue:_value]];
462 if (_colname2 != nil) {
463 [sql appendString:@" AND "];
464 [sql appendString:_colname2];
465 [sql appendString:@" = "];
466 [sql appendString:[self _formatRowValue:_value2]];
472 - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name
473 baseVersion:(unsigned int)_baseVersion
475 EOAdaptorChannel *storeChannel, *quickChannel;
476 NSMutableDictionary *quickRow, *contentRow;
477 GCSFieldExtractor *extractor;
479 NSNumber *storedVersion;
481 NSCalendarDate *nowDate;
483 NSString *qsql, *bsql;
485 /* check preconditions */
488 return [NSException exceptionWithName:@"GCSStoreException"
489 reason:@"no content filename was provided"
492 if (_content == nil) {
493 return [NSException exceptionWithName:@"GCSStoreException"
494 reason:@"no content was provided"
501 nowDate = [NSCalendarDate date];
502 now = [NSNumber numberWithUnsignedInt:[nowDate timeIntervalSince1970]];
505 [self logWithFormat:@"should store content: '%@'\n%@", _name, _content];
507 storedVersion = [self versionOfContentWithName:_name];
509 [self logWithFormat:@" version: %@", storedVersion];
510 isNewRecord = [storedVersion isNotNull] ? NO : YES;
512 /* check whether sequence matches */
514 if (_baseVersion != 0 /* use 0 to override check */) {
515 if (_baseVersion != [storedVersion unsignedIntValue]) {
516 /* version mismatch (concurrent update) */
517 return [self errorVersionMismatchBetweenStoredVersion:
518 [storedVersion unsignedIntValue]
519 andExpectedVersion:_baseVersion];
523 /* extract quick info */
525 extractor = [self->folderInfo quickExtractor];
526 if ((quickRow = [extractor extractQuickFieldsFromContent:_content]) == nil) {
527 return [self errorExtractorReturnedNoQuickRow:extractor
528 forContent:_content];
531 [quickRow setObject:_name forKey:@"c_name"];
534 [self logWithFormat:@" store quick: %@", quickRow];
536 /* make content row */
538 contentRow = [NSMutableDictionary dictionaryWithCapacity:16];
540 if (self->ofFlags.sameTableForQuick)
541 [contentRow addEntriesFromDictionary:quickRow];
543 [contentRow setObject:_name forKey:@"c_name"];
544 if (isNewRecord) [contentRow setObject:now forKey:@"c_creationdate"];
545 [contentRow setObject:now forKey:@"c_lastmodified"];
547 [contentRow setObject:[NSNumber numberWithInt:0] forKey:@"c_version"];
549 // TODO: increase version?
550 [contentRow setObject:
551 [NSNumber numberWithInt:([storedVersion intValue] + 1)]
552 forKey:@"c_version"];
554 [contentRow setObject:_content forKey:@"c_content"];
558 if ((storeChannel = [self acquireStoreChannel]) == nil) {
559 [self errorWithFormat:@"%s: could not open storage channel!",
560 __PRETTY_FUNCTION__];
563 if (!self->ofFlags.sameTableForQuick) {
564 if ((quickChannel = [self acquireQuickChannel]) == nil) {
565 [self errorWithFormat:@"%s: could not open quick channel!",
566 __PRETTY_FUNCTION__];
567 [self releaseChannel:storeChannel];
575 if (isNewRecord) { /* insert */
576 if (!self->ofFlags.sameTableForQuick) {
577 qsql = [self _generateInsertStatementForRow:quickRow
578 tableName:[self quickTableName]];
580 bsql = [self _generateInsertStatementForRow:contentRow
581 tableName:[self storeTableName]];
584 if (!self->ofFlags.sameTableForQuick) {
585 qsql = [self _generateUpdateStatementForRow:quickRow
586 tableName:[self quickTableName]
587 whereColumn:@"c_name" isEqualTo:_name
588 andColumn:nil isEqualTo:nil];
591 /* also ensure in the DB that the version keeps staying the same */
592 bsql = [self _generateUpdateStatementForRow:contentRow
593 tableName:[self storeTableName]
594 whereColumn:@"c_name" isEqualTo:_name
595 andColumn:(_baseVersion != 0 ? (id)@"c_version" : (id)nil)
596 isEqualTo:(_baseVersion != 0
597 ? [NSNumber numberWithUnsignedInt:_baseVersion]
602 // TODO: execute in transactions
604 if ((error = [storeChannel evaluateExpressionX:bsql]) != nil) {
605 [self logWithFormat:@"ERROR(%s): cannot %s content '%@': %@",
606 __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update", bsql, error];
608 // TODO: should check whether a row was actually updated?
610 if (error == nil && qsql != nil) {
611 if ((error = [quickChannel evaluateExpressionX:qsql]) != nil) {
615 [self logWithFormat:@"ERROR(%s): cannot %s quick '%@': %@",
616 __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update",
620 /* insert in quick failed, so delete in content table */
622 delsql = [@"DELETE FROM " stringByAppendingString:
623 [self storeTableName]];
624 delsql = [delsql stringByAppendingString:@" WHERE c_name="];
625 delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
626 if ((delErr = [storeChannel evaluateExpressionX:delsql]) != nil) {
627 [self errorWithFormat:
628 @"%s: could not delete content '%@' after quick-fail:"
629 @" %@", __PRETTY_FUNCTION__, delsql, error];
635 [self releaseChannel:storeChannel];
636 if (!self->ofFlags.sameTableForQuick) [self releaseChannel:quickChannel];
641 - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name {
642 /* this method does not check for concurrent writes */
643 return [self writeContent:_content toName:_name baseVersion:0];
646 - (NSException *)deleteContentWithName:(NSString *)_name {
647 EOAdaptorChannel *storeChannel, *quickChannel;
651 /* check preconditions */
654 return [NSException exceptionWithName:@"GCSDeleteException"
655 reason:@"no content filename was provided"
660 [self logWithFormat:@"should delete content: '%@'", _name];
664 if ((storeChannel = [self acquireStoreChannel]) == nil) {
665 [self errorWithFormat:@"could not open storage channel!"];
668 if (!self->ofFlags.sameTableForQuick) {
669 if ((quickChannel = [self acquireQuickChannel]) == nil) {
670 [self errorWithFormat:@"could not open quick channel!"];
671 [self releaseChannel:storeChannel];
678 delsql = [@"DELETE FROM " stringByAppendingString:[self storeTableName]];
679 delsql = [delsql stringByAppendingString:@" WHERE c_name="];
680 delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
681 if ((error = [storeChannel evaluateExpressionX:delsql]) != nil) {
682 [self errorWithFormat:
683 @"%s: cannot delete content '%@': %@",
684 __PRETTY_FUNCTION__, delsql, error];
686 else if (!self->ofFlags.sameTableForQuick) {
687 /* content row deleted, now delete the quick row */
688 delsql = [@"DELETE FROM " stringByAppendingString:[self quickTableName]];
689 delsql = [delsql stringByAppendingString:@" WHERE c_name="];
690 delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
691 if ((error = [quickChannel evaluateExpressionX:delsql]) != nil) {
692 [self errorWithFormat:
693 @"%s: cannot delete quick row '%@': %@",
694 __PRETTY_FUNCTION__, delsql, error];
696 Note: we now have a "broken" record, needs to be periodically GCed by
702 /* release channels and return */
704 [self releaseChannel:storeChannel];
705 if (!self->ofFlags.sameTableForQuick)
706 [self releaseChannel:quickChannel];
710 - (NSException *)deleteFolder {
711 EOAdaptorChannel *channel;
717 if ((channel = [self acquireStoreChannel]) == nil) {
718 [self errorWithFormat:@"could not open channel!"];
724 table = [self storeTableName];
725 if ([table length] > 0) {
726 delsql = [@"DROP TABLE " stringByAppendingString: table];
727 [channel evaluateExpressionX:delsql];
729 table = [self quickTableName];
730 if ([table length] > 0) {
731 delsql = [@"DROP TABLE " stringByAppendingString: table];
732 [channel evaluateExpressionX:delsql];
734 table = [self aclTableName];
735 if ([table length] > 0) {
736 delsql = [@"DROP TABLE " stringByAppendingString: table];
737 [channel evaluateExpressionX:delsql];
740 [self releaseChannel:channel];
745 - (NSString *)columnNameForFieldName:(NSString *)_fieldName {
751 - (NSString *)generateSQLForSortOrderings:(NSArray *)_so {
752 NSMutableString *sql;
755 if ((count = [_so count]) == 0)
758 sql = [NSMutableString stringWithCapacity:(count * 16)];
759 for (i = 0; i < count; i++) {
764 so = [_so objectAtIndex:i];
766 column = [self columnNameForFieldName:[so key]];
768 if (i > 0) [sql appendString:@", "];
770 if (sel_eq(sel, EOCompareAscending)) {
771 [sql appendString:column];
772 [sql appendString:@" ASC"];
774 else if (sel_eq(sel, EOCompareDescending)) {
775 [sql appendString:column];
776 [sql appendString:@" DESC"];
778 else if (sel_eq(sel, EOCompareCaseInsensitiveAscending)) {
779 [sql appendString:@"UPPER("];
780 [sql appendString:column];
781 [sql appendString:@") ASC"];
783 else if (sel_eq(sel, EOCompareCaseInsensitiveDescending)) {
784 [sql appendString:@"UPPER("];
785 [sql appendString:column];
786 [sql appendString:@") DESC"];
789 [self logWithFormat:@"cannot handle sort selector in store: %@",
790 NSStringFromSelector(sel)];
796 - (NSString *)generateSQLForQualifier:(EOQualifier *)_q {
799 if (_q == nil) return nil;
800 ms = [NSMutableString stringWithCapacity:32];
801 [_q _gcsAppendToString:ms];
807 - (NSArray *)fetchFields:(NSArray *)_flds
808 fetchSpecification:(EOFetchSpecification *)_fs
810 EOQualifier *qualifier;
811 NSArray *sortOrderings;
812 EOAdaptorChannel *channel;
814 NSMutableString *sql;
816 NSMutableArray *results;
819 qualifier = [_fs qualifier];
820 sortOrderings = [_fs sortOrderings];
823 [self logWithFormat:@"FETCH: %@", _flds];
824 [self logWithFormat:@" MATCH: %@", _q];
829 sql = [NSMutableString stringWithCapacity:256];
830 [sql appendString:@"SELECT "];
832 [sql appendString:@"*"];
836 count = [_flds count];
837 for (i = 0; i < count; i++) {
838 if (i > 0) [sql appendString:@", "];
839 [sql appendString:[self columnNameForFieldName:[_flds objectAtIndex:i]]];
842 [sql appendString:@" FROM "];
843 [sql appendString:[self quickTableName]];
845 if (qualifier != nil) {
846 [sql appendString:@" WHERE "];
847 [sql appendString:[self generateSQLForQualifier:qualifier]];
849 if ([sortOrderings count] > 0) {
850 [sql appendString:@" ORDER BY "];
851 [sql appendString:[self generateSQLForSortOrderings:sortOrderings]];
855 [sql appendString:@" LIMIT "]; // count
856 [sql appendString:@" OFFSET "]; // index from 0
861 if ((channel = [self acquireStoreChannel]) == nil) {
862 [self errorWithFormat:@" could not open storage channel!"];
868 if ((error = [channel evaluateExpressionX:sql]) != nil) {
869 [self errorWithFormat:@"%s: cannot execute quick-fetch SQL '%@': %@",
870 __PRETTY_FUNCTION__, sql, error];
871 [self releaseChannel:channel];
877 results = [NSMutableArray arrayWithCapacity:64];
878 attrs = [channel describeResults:NO /* do not beautify names */];
879 while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil)
880 [results addObject:row];
882 /* release channels */
884 [self releaseChannel:channel];
888 - (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q {
889 EOFetchSpecification *fs;
894 fs = [EOFetchSpecification fetchSpecificationWithEntityName:
899 return [self fetchFields:_flds fetchSpecification:fs];
902 - (NSArray *)fetchAclWithSpecification:(EOFetchSpecification *)_fs {
903 EOQualifier *qualifier;
904 NSArray *sortOrderings;
905 EOAdaptorChannel *channel;
907 NSMutableString *sql;
909 NSMutableArray *results;
912 qualifier = [_fs qualifier];
913 sortOrderings = [_fs sortOrderings];
916 [self logWithFormat:@"FETCH: %@", _flds];
917 [self logWithFormat:@" MATCH: %@", _q];
922 sql = [NSMutableString stringWithCapacity:256];
923 [sql appendString:@"SELECT c_uid, c_object, c_role"];
924 [sql appendString:@" FROM "];
925 [sql appendString:[self aclTableName]];
927 if (qualifier != nil) {
928 [sql appendString:@" WHERE "];
929 [sql appendString:[self generateSQLForQualifier:qualifier]];
931 if ([sortOrderings count] > 0) {
932 [sql appendString:@" ORDER BY "];
933 [sql appendString:[self generateSQLForSortOrderings:sortOrderings]];
937 [sql appendString:@" LIMIT "]; // count
938 [sql appendString:@" OFFSET "]; // index from 0
943 if ((channel = [self acquireAclChannel]) == nil) {
944 [self errorWithFormat:@"could not open acl channel!"];
950 if ((error = [channel evaluateExpressionX:sql]) != nil) {
951 [self errorWithFormat:@"%s: cannot execute acl-fetch SQL '%@': %@",
952 __PRETTY_FUNCTION__, sql, error];
953 [self releaseChannel:channel];
959 results = [NSMutableArray arrayWithCapacity:64];
960 attrs = [channel describeResults:NO /* do not beautify names */];
961 while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil)
962 [results addObject:row];
964 /* release channels */
966 [self releaseChannel:channel];
970 - (NSArray *) fetchAclMatchingQualifier:(EOQualifier *)_q {
971 EOFetchSpecification *fs;
976 fs = [EOFetchSpecification fetchSpecificationWithEntityName:
981 return [self fetchAclWithSpecification:fs];
984 - (void) deleteAclMatchingQualifier:(EOQualifier *)_q {
985 EOFetchSpecification *fs;
988 fs = [EOFetchSpecification fetchSpecificationWithEntityName:
992 [self deleteAclWithSpecification:fs];
996 - (void)deleteAclWithSpecification:(EOFetchSpecification *)_fs
998 EOQualifier *qualifier;
999 EOAdaptorChannel *channel;
1001 NSMutableString *sql;
1003 qualifier = [_fs qualifier];
1004 if (qualifier != nil) {
1005 sql = [NSMutableString stringWithCapacity:256];
1006 [sql appendString:@"DELETE FROM "];
1007 [sql appendString:[self aclTableName]];
1008 [sql appendString:@" WHERE "];
1009 [sql appendString:[self generateSQLForQualifier:qualifier]];
1014 if ((channel = [self acquireAclChannel]) == nil) {
1015 [self errorWithFormat:@"could not open acl channel!"];
1021 if ((error = [channel evaluateExpressionX:sql]) != nil) {
1022 [self errorWithFormat:@"%s: cannot execute acl-fetch SQL '%@': %@",
1023 __PRETTY_FUNCTION__, sql, error];
1024 [self releaseChannel:channel];
1028 [self releaseChannel:channel];
1033 - (NSString *)description {
1034 NSMutableString *ms;
1037 ms = [NSMutableString stringWithCapacity:256];
1038 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
1041 [ms appendFormat:@" id=%@", self->folderId];
1043 [ms appendString:@" no-id"];
1045 if ((tmp = [self path])) [ms appendFormat:@" path=%@", tmp];
1046 if ((tmp = [self folderTypeName])) [ms appendFormat:@" type=%@", tmp];
1047 if ((tmp = [self location]))
1048 [ms appendFormat:@" loc=%@", [tmp absoluteString]];
1050 [ms appendString:@">"];
1054 @end /* GCSFolder */