From a9c58c930833cc8170d3ce0e14761f6f51b3eb5f Mon Sep 17 00:00:00 2001 From: helge Date: Fri, 2 Jul 2004 17:41:41 +0000 Subject: [PATCH] git-svn-id: http://svn.opengroupware.org/SOGo/trunk@136 d1b88da0-ebda-0310-925b-ed51d893ca5b --- OGoContentStore/ChangeLog | 4 + OGoContentStore/EOQualifier+OCS.h | 36 ++++ OGoContentStore/EOQualifier+OCS.m | 118 +++++++++++ OGoContentStore/GNUmakefile | 1 + OGoContentStore/OCSChannelManager.m | 69 ++++++- OGoContentStore/OCSFolder.h | 2 - OGoContentStore/OCSFolder.m | 303 +++++++++++++++++++--------- OGoContentStore/README | 7 +- 8 files changed, 432 insertions(+), 108 deletions(-) create mode 100644 OGoContentStore/EOQualifier+OCS.h create mode 100644 OGoContentStore/EOQualifier+OCS.m diff --git a/OGoContentStore/ChangeLog b/OGoContentStore/ChangeLog index 8c983633..c6cb08fa 100644 --- a/OGoContentStore/ChangeLog +++ b/OGoContentStore/ChangeLog @@ -1,3 +1,7 @@ +2004-07-02 Helge Hess + + * OCSChannelManager.m: added garbage collector for channel pools + 2004-06-30 Helge Hess * OCSChannelManager.m: implemented pooling diff --git a/OGoContentStore/EOQualifier+OCS.h b/OGoContentStore/EOQualifier+OCS.h new file mode 100644 index 00000000..f1eb13e3 --- /dev/null +++ b/OGoContentStore/EOQualifier+OCS.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2004 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ +// $Id$ + +#ifndef __OGoContentStore_EOQualifier_OCS_H__ +#define __OGoContentStore_EOQualifier_OCS_H__ + +#include + +@class NSMutableString; + +@interface EOQualifier(OCS) + +- (void)_ocsAppendToString:(NSMutableString *)_ms; + +@end + +#endif /* __OGoContentStore_EOQualifier_OCS_H__ */ diff --git a/OGoContentStore/EOQualifier+OCS.m b/OGoContentStore/EOQualifier+OCS.m new file mode 100644 index 00000000..68e6caa9 --- /dev/null +++ b/OGoContentStore/EOQualifier+OCS.m @@ -0,0 +1,118 @@ +/* + Copyright (C) 2004 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ +// $Id$ + +#include "EOQualifier+OCS.h" +#include "common.h" + +@implementation EOQualifier(OCS) + +- (void)_appendAndQualifier:(EOAndQualifier *)_q + toString:(NSMutableString *)_ms +{ + // TODO: move to EOQualifier category + NSArray *qs; + unsigned i, count; + + qs = [_q qualifiers]; + if ((count = [qs count]) == 0) + return; + + for (i = 0; i < count; i++) { + if (i != 0) [_ms appendString:@" AND "]; + if (count > 1) [_ms appendString:@"("]; + [[qs objectAtIndex:i] _ocsAppendToString:_ms]; + if (count > 1) [_ms appendString:@")"]; + } +} +- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q + toString:(NSMutableString *)_ms +{ + id val; + + [_ms appendString:[_q key]]; + + if ((val = [_q value])) { + SEL op = [_q selector]; + + if ([val isNotNull]) { + if (sel_eq(op, EOQualifierOperatorEqual)) + [_ms appendString:@" = "]; + else if (sel_eq(op, EOQualifierOperatorNotEqual)) + [_ms appendString:@" != "]; + else if (sel_eq(op, EOQualifierOperatorLessThan)) + [_ms appendString:@" < "]; + else if (sel_eq(op, EOQualifierOperatorGreaterThan)) + [_ms appendString:@" > "]; + else if (sel_eq(op, EOQualifierOperatorLessThanOrEqualTo)) + [_ms appendString:@" <= "]; + else if (sel_eq(op, EOQualifierOperatorGreaterThanOrEqualTo)) + [_ms appendString:@" >= "]; + else if (sel_eq(op, EOQualifierOperatorLike)) + [_ms appendString:@" LIKE "]; + else { + [self logWithFormat:@"ERROR(%s): unsupported operation for null: %@", + __PRETTY_FUNCTION__, NSStringFromSelector(op)]; + } + + if ([val isKindOfClass:[NSNumber class]]) + [_ms appendString:[val stringValue]]; + else if ([val isKindOfClass:[NSString class]]) { + [_ms appendString:@"'"]; + [_ms appendString:val]; + [_ms appendString:@"'"]; + } + else { + [self logWithFormat:@"ERROR(%s): unsupported value class: %@", + __PRETTY_FUNCTION__, NSStringFromClass([val class])]; + } + } + else { + if (sel_eq(op, EOQualifierOperatorEqual)) + [_ms appendString:@" IS NULL"]; + else if (sel_eq(op, EOQualifierOperatorEqual)) + [_ms appendString:@" IS NOT NULL"]; + else { + [self logWithFormat:@"ERROR(%s): invalid operation for null: %@", + __PRETTY_FUNCTION__, NSStringFromSelector(op)]; + } + } + } + else + [_ms appendString:@" IS NULL"]; +} + +- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms { + if (_q == nil) return; + + if ([_q isKindOfClass:[EOAndQualifier class]]) + [self _appendAndQualifier:(id)_q toString:_ms]; + else if ([_q isKindOfClass:[EOKeyValueQualifier class]]) + [self _appendKeyValueQualifier:(id)_q toString:_ms]; + else + NSLog(@"ERROR: unknown qualifier: %@", _q); +} + +- (void)_ocsAppendToString:(NSMutableString *)_ms { + [self _appendQualifier:self toString:_ms]; +} + +@end /* EOQualifier(OCS) */ diff --git a/OGoContentStore/GNUmakefile b/OGoContentStore/GNUmakefile index 5e9c0651..3244dda3 100644 --- a/OGoContentStore/GNUmakefile +++ b/OGoContentStore/GNUmakefile @@ -25,6 +25,7 @@ libOGoContentStore_HEADER_FILES += \ libOGoContentStore_OBJC_FILES += \ NSURL+OCS.m \ EOAdaptorChannel+OCS.m \ + EOQualifier+OCS.m \ \ OCSContext.m \ OCSFieldInfo.m \ diff --git a/OGoContentStore/OCSChannelManager.m b/OGoContentStore/OCSChannelManager.m index e51f9c4e..3d9a28bf 100644 --- a/OGoContentStore/OCSChannelManager.m +++ b/OGoContentStore/OCSChannelManager.m @@ -22,6 +22,7 @@ #include "OCSChannelManager.h" #include "NSURL+OCS.h" +#include "EOAdaptorChannel+OCS.h" #include #include #include @@ -52,18 +53,25 @@ @implementation OCSChannelManager -static BOOL debugOn = YES; -static BOOL debugPools = YES; -static int ChannelExpireAge = 180; +static BOOL debugOn = NO; +static BOOL debugPools = NO; +static int ChannelExpireAge = 180; +static NSTimeInterval ChannelCollectionTimer = 5 * 60; + (void)initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; debugOn = [ud boolForKey:@"OCSChannelManagerDebugEnabled"]; debugPools = [ud boolForKey:@"OCSChannelManagerPoolDebugEnabled"]; + ChannelExpireAge = [[ud objectForKey:@"OCSChannelExpireAge"] intValue]; if (ChannelExpireAge < 1) ChannelExpireAge = 180; + + ChannelCollectionTimer = + [[ud objectForKey:@"OCSChannelCollectionTimer"] intValue]; + if (ChannelCollectionTimer < 1) + ChannelCollectionTimer = 5*60; } + (NSString *)adaptorNameForURLScheme:(NSString *)_scheme { @@ -83,6 +91,11 @@ static int ChannelExpireAge = 180; self->urlToAdaptor = [[NSMutableDictionary alloc] initWithCapacity:4]; self->availableChannels = [[NSMutableArray alloc] initWithCapacity:16]; self->busyChannels = [[NSMutableArray alloc] initWithCapacity:16]; + + self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval: + ChannelCollectionTimer + target:self selector:@selector(_garbageCollect:) + userInfo:nil repeats:YES] retain]; } return self; } @@ -300,9 +313,11 @@ static int ChannelExpireAge = 180; [handle release]; return; } - - [self logWithFormat: - @"DBPOOL: freeing old channel (age %ds)", (int)[handle age]]; + + if (debugPools) { + [self logWithFormat: + @"DBPOOL: freeing old channel (age %ds)", (int)[handle age]]; + } /* not reusing channel */ [handle release]; handle = nil; @@ -345,6 +360,48 @@ static int ChannelExpireAge = 180; return result; } +/* collect old channels */ + +- (void)_garbageCollect:(NSTimer *)_timer { + NSMutableArray *handlesToRemove; + unsigned i, count; + + if ((count = [self->availableChannels count]) == 0) + /* no available channels */ + return; + + /* collect channels to expire */ + + handlesToRemove = [[NSMutableArray alloc] initWithCapacity:4]; + for (i = 0; i < count; i++) { + OCSChannelHandle *handle; + + handle = [self->availableChannels objectAtIndex:i]; + if (![[handle channel] isOpen]) { + [handlesToRemove addObject:handle]; + continue; + } + if ([handle age] > ChannelExpireAge) { + [handlesToRemove addObject:handle]; + continue; + } + } + + /* remove channels */ + count = [handlesToRemove count]; + if (debugPools) + [self logWithFormat:@"DBPOOL: garbage collecting %d channels.", count]; + for (i = 0; i < count; i++) { + OCSChannelHandle *handle; + + handle = [[self->availableChannels objectAtIndex:i] retain]; + [self->availableChannels removeObject:handle]; + if ([[handle channel] isOpen]) + [[handle channel] closeChannel]; + [handle release]; + } +} + /* debugging */ - (BOOL)isDebuggingEnabled { diff --git a/OGoContentStore/OCSFolder.h b/OGoContentStore/OCSFolder.h index 7453f399..627f58ed 100644 --- a/OGoContentStore/OCSFolder.h +++ b/OGoContentStore/OCSFolder.h @@ -82,8 +82,6 @@ - (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q; -- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms; - @end #endif /* __OGoContentStore_OCSFolder_H__ */ diff --git a/OGoContentStore/OCSFolder.m b/OGoContentStore/OCSFolder.m index 3fa18634..f7dfce4b 100644 --- a/OGoContentStore/OCSFolder.m +++ b/OGoContentStore/OCSFolder.m @@ -27,16 +27,19 @@ #include "OCSFieldExtractor.h" #include "NSURL+OCS.h" #include "EOAdaptorChannel+OCS.h" +#include "EOQualifier+OCS.h" #include "common.h" @implementation OCSFolder -static BOOL debugOn = YES; +static BOOL debugOn = YES; +static BOOL doLogStore = YES; + (void)initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - debugOn = [ud boolForKey:@"OCSFolderDebugEnabled"]; + debugOn = [ud boolForKey:@"OCSFolderDebugEnabled"]; + doLogStore = [ud boolForKey:@"OCSFolderStoreDebugEnabled"]; } - (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId @@ -146,7 +149,9 @@ static BOOL debugOn = YES; recursive:YES]; } -- (NSString *)fetchContentWithName:(NSString *)_name { +- (id)_fetchValueOfColumn:(NSString *)_col attributeName:(NSString *)_attrName + inContentWithName:(NSString *)_name +{ EOAdaptorChannel *channel; NSException *error; NSDictionary *row; @@ -161,16 +166,18 @@ static BOOL debugOn = YES; /* generate SQL */ - sql = @"SELECT \"c_content\" FROM "; + sql = @"SELECT "; + sql = [sql stringByAppendingString:_col]; + sql = [sql stringByAppendingString:@" FROM "]; sql = [sql stringByAppendingString:[self storeTableName]]; sql = [sql stringByAppendingString:@" WHERE \"c_name\" = '"]; sql = [sql stringByAppendingString:_name]; sql = [sql stringByAppendingString:@"'"]; /* run SQL */ - + if ((error = [channel evaluateExpressionX:sql]) != nil) { - [self logWithFormat:@"ERROR(%s): cannot execute blob-fetch SQL '%@': %@", + [self logWithFormat:@"ERROR(%s): cannot execute SQL '%@': %@", __PRETTY_FUNCTION__, sql, error]; [self releaseChannel:channel]; return nil; @@ -181,7 +188,7 @@ static BOOL debugOn = YES; result = nil; attrs = [channel describeResults]; if ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) { - result = [[[row objectForKey:@"cContent"] copy] autorelease]; + result = [[[row objectForKey:_attrName] copy] autorelease]; if (![result isNotNull]) result = nil; [channel cancelFetch]; } @@ -192,19 +199,159 @@ static BOOL debugOn = YES; return result; } +- (NSNumber *)versionOfContentWithName:(NSString *)_name { + return [self _fetchValueOfColumn:@"c_version" attributeName:@"cVersion" + inContentWithName:_name]; +} + +- (NSString *)fetchContentWithName:(NSString *)_name { + return [self _fetchValueOfColumn:@"c_content" attributeName:@"cContent" + inContentWithName:_name]; +} + +/* writing content */ + +- (NSString *)_formatRowValue:(id)_value { + if (![_value isNotNull]) + return @"NULL"; + + if ([_value isKindOfClass:[NSString class]]) + return [NSString stringWithFormat:@"'%@'", _value]; + + if ([_value isKindOfClass:[NSNumber class]]) + return [_value stringValue]; + + if ([_value isKindOfClass:[NSCalendarDate class]]) { + /* be smart ... convert to timestamp */ + return [NSString stringWithFormat:@"%i", [_value timeIntervalSince1970]]; + } + + [self logWithFormat:@"cannot handle value class: %@", [_value class]]; + return nil; +} + +- (NSString *)_generateInsertStatementForRow:(NSDictionary *)_row + tableName:(NSString *)_table +{ + // TODO: move to NSDictionary category? + NSMutableString *sql; + NSArray *keys; + unsigned i, count; + + if (_row == nil || _table == nil) + return nil; + + keys = [_row allKeys]; + + sql = [NSMutableString stringWithCapacity:512]; + [sql appendString:@"INSERT INTO "]; + [sql appendString:_table]; + [sql appendString:@" ("]; + + for (i = 0, count = [keys count]; i < count; i++) { + if (i != 0) [sql appendString:@", "]; + [sql appendString:[keys objectAtIndex:i]]; + } + + [sql appendString:@") VALUES ("]; + + for (i = 0, count = [keys count]; i < count; i++) { + id value; + + if (i != 0) [sql appendString:@", "]; + value = [_row objectForKey:[keys objectAtIndex:i]]; + value = [self _formatRowValue:value]; + [sql appendString:value]; + } + + [sql appendString:@")"]; + return sql; +} + +- (NSString *)_generateUpdateStatementForRow:(NSDictionary *)_row + tableName:(NSString *)_table + whereColumn:(NSString *)_colname isEqualTo:(id)_value +{ + // TODO: move to NSDictionary category? + NSMutableString *sql; + NSArray *keys; + unsigned i, count; + + if (_row == nil || _table == nil) + return nil; + + keys = [_row allKeys]; + + sql = [NSMutableString stringWithCapacity:512]; + [sql appendString:@"UPDATE "]; + [sql appendString:_table]; + + [sql appendString:@" SET "]; + for (i = 0, count = [keys count]; i < count; i++) { + id value; + + value = [_row objectForKey:[keys objectAtIndex:i]]; + value = [self _formatRowValue:value]; + + if (i != 0) [sql appendString:@", "]; + [sql appendString:[keys objectAtIndex:i]]; + [sql appendString:@" = "]; + [sql appendString:value]; + } + + [sql appendString:@" WHERE "]; + [sql appendString:_colname]; + [sql appendString:@" = "]; + [sql appendString:[self _formatRowValue:_value]]; + + return sql; +} + - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name { EOAdaptorChannel *storeChannel, *quickChannel; - NSMutableDictionary *quickRow; + NSMutableDictionary *quickRow, *contentRow; OCSFieldExtractor *extractor; + NSException *error; + NSNumber *storedVersion; + BOOL isNewRecord; + NSCalendarDate *nowDate; + NSNumber *now; + NSString *qsql, *bsql; - [self logWithFormat:@"SHOULD store content:\n%@", _content]; + error = nil; + nowDate = [NSCalendarDate date]; + now = [NSNumber numberWithUnsignedInt:[nowDate timeIntervalSince1970]]; - /* extract info */ + if (doLogStore) + [self logWithFormat:@"SHOULD store content: %@\n%@", _name, _content]; + + storedVersion = [self versionOfContentWithName:_name]; + if (doLogStore) + [self logWithFormat:@" version: %@", storedVersion]; + isNewRecord = [storedVersion isNotNull] ? NO : YES; + + /* extract quick info */ extractor = [self->folderInfo quickExtractor]; quickRow = [extractor extractQuickFieldsFromContent:_content]; - - [self logWithFormat:@"SHOULD store quick: %@", quickRow]; + [quickRow setObject:_name forKey:@"c_name"]; + + if (doLogStore) + [self logWithFormat:@" store quick: %@", quickRow]; + + /* make content row */ + + contentRow = [NSMutableDictionary dictionaryWithCapacity:16]; + [contentRow setObject:_name forKey:@"c_name"]; + if (isNewRecord) [contentRow setObject:now forKey:@"c_creationdate"]; + [contentRow setObject:now forKey:@"c_lastmodified"]; + if (isNewRecord) + [contentRow setObject:[NSNumber numberWithInt:0] forKey:@"c_version"]; + else { + [contentRow setObject:[NSNumber numberWithInt:[storedVersion intValue]] + forKey:@"c_version"]; + } + [contentRow setObject:_content forKey:@"c_content"]; /* open channels */ @@ -219,13 +366,54 @@ static BOOL debugOn = YES; } // TODO: gen SQL, execute in transactions + if (isNewRecord) { /* insert */ + qsql = [self _generateInsertStatementForRow:quickRow + tableName:[self quickTableName]]; + bsql = [self _generateInsertStatementForRow:contentRow + tableName:[self storeTableName]]; + + if ((error = [storeChannel evaluateExpressionX:bsql]) != nil) { + [self logWithFormat:@"ERROR(%s): cannot insert content '%@': %@", + __PRETTY_FUNCTION__, bsql, error]; + } + else if ((error = [quickChannel evaluateExpressionX:qsql]) != nil) { + NSString *delsql; + NSException *delErr; + + [self logWithFormat:@"ERROR(%s): cannot insert quick '%@': %@", + __PRETTY_FUNCTION__, qsql, error]; + + delsql = [@"DELETE FROM " stringByAppendingString:[self storeTableName]]; + delsql = [delsql stringByAppendingString:@" WHERE c_name="]; + delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]]; + if ((delErr = [storeChannel evaluateExpressionX:delsql]) != nil) { + [self logWithFormat: + @"ERROR(%s): cannot delete content '%@' after quick-fail: %@", + __PRETTY_FUNCTION__, delsql, error]; + } + } + } + else { /* update */ + qsql = [self _generateUpdateStatementForRow:quickRow + tableName:[self quickTableName] + whereColumn:@"c_name" isEqualTo:_name]; + bsql = [self _generateUpdateStatementForRow:contentRow + tableName:[self storeTableName] + whereColumn:@"c_name" isEqualTo:_name]; + if ((error = [storeChannel evaluateExpressionX:bsql]) != nil) { + [self logWithFormat:@"ERROR(%s): cannot update content '%@': %@", + __PRETTY_FUNCTION__, bsql, error]; + } + else if ((error = [quickChannel evaluateExpressionX:qsql]) != nil) { + [self logWithFormat:@"ERROR(%s): cannot update quick '%@': %@", + __PRETTY_FUNCTION__, qsql, error]; + } + } [self releaseChannel:storeChannel]; [self releaseChannel:quickChannel]; - return [NSException exceptionWithName:@"NotYetImplemented" - reason:@"no time, no money, ..." - userInfo:nil]; + return error; } - (NSString *)columnNameForFieldName:(NSString *)_fieldName { @@ -234,96 +422,13 @@ static BOOL debugOn = YES; /* SQL generation */ -- (void)_appendAndQualifier:(EOAndQualifier *)_q - toString:(NSMutableString *)_ms -{ - NSArray *qs; - unsigned i, count; - - qs = [_q qualifiers]; - if ((count = [qs count]) == 0) - return; - - for (i = 0; i < count; i++) { - if (i != 0) [_ms appendString:@" AND "]; - if (count > 1) [_ms appendString:@"("]; - [self _appendQualifier:[qs objectAtIndex:i] toString:_ms]; - if (count > 1) [_ms appendString:@")"]; - } -} -- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q - toString:(NSMutableString *)_ms -{ - id val; - - [_ms appendString:[_q key]]; - - if ((val = [_q value])) { - SEL op = [_q selector]; - - if ([val isNotNull]) { - if (sel_eq(op, EOQualifierOperatorEqual)) - [_ms appendString:@" = "]; - else if (sel_eq(op, EOQualifierOperatorNotEqual)) - [_ms appendString:@" != "]; - else if (sel_eq(op, EOQualifierOperatorLessThan)) - [_ms appendString:@" < "]; - else if (sel_eq(op, EOQualifierOperatorGreaterThan)) - [_ms appendString:@" > "]; - else if (sel_eq(op, EOQualifierOperatorLessThanOrEqualTo)) - [_ms appendString:@" <= "]; - else if (sel_eq(op, EOQualifierOperatorGreaterThanOrEqualTo)) - [_ms appendString:@" >= "]; - else if (sel_eq(op, EOQualifierOperatorLike)) - [_ms appendString:@" LIKE "]; - else { - [self logWithFormat:@"ERROR(%s): unsupported operation for null: %@", - __PRETTY_FUNCTION__, NSStringFromSelector(op)]; - } - - if ([val isKindOfClass:[NSNumber class]]) - [_ms appendString:[val stringValue]]; - else if ([val isKindOfClass:[NSString class]]) { - [_ms appendString:@"'"]; - [_ms appendString:val]; - [_ms appendString:@"'"]; - } - else { - [self logWithFormat:@"ERROR(%s): unsupported value class: %@", - __PRETTY_FUNCTION__, NSStringFromClass([val class])]; - } - } - else { - if (sel_eq(op, EOQualifierOperatorEqual)) - [_ms appendString:@" IS NULL"]; - else if (sel_eq(op, EOQualifierOperatorEqual)) - [_ms appendString:@" IS NOT NULL"]; - else { - [self logWithFormat:@"ERROR(%s): invalid operation for null: %@", - __PRETTY_FUNCTION__, NSStringFromSelector(op)]; - } - } - } - else - [_ms appendString:@" IS NULL"]; -} -- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms { - if (_q == nil) return; - - if ([_q isKindOfClass:[EOAndQualifier class]]) - [self _appendAndQualifier:(id)_q toString:_ms]; - else if ([_q isKindOfClass:[EOKeyValueQualifier class]]) - [self _appendKeyValueQualifier:(id)_q toString:_ms]; - else - NSLog(@"ERROR: unknown qualifier: %@", _q); -} - (NSString *)generateSQLForQualifier:(EOQualifier *)_q { NSMutableString *ms; if (_q == nil) return nil; ms = [NSMutableString stringWithCapacity:32]; - [self _appendQualifier:_q toString:ms]; + [_q _ocsAppendToString:ms]; return ms; } diff --git a/OGoContentStore/README b/OGoContentStore/README index 2fd0d419..ef64dd9c 100644 --- a/OGoContentStore/README +++ b/OGoContentStore/README @@ -49,7 +49,12 @@ Defaults OCSChannelManagerDebugEnabled - enable channel debug pooling logs OCSChannelManagerPoolDebugEnabled - debug pool handle allocation - + + OCSChannelExpireAge - if that age in seconds is exceeded, a channel + will be removed from the pool + OCSChannelCollectionTimer - time in seconds. each n-seconds the pool will be + checked for channels too old + [PGDebugEnabled] - enable PostgreSQL adaptor debugging URLs -- 2.39.5