]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@136 d1b88da0-ebda-0310-925b-ed51d...
authorhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 2 Jul 2004 17:41:41 +0000 (17:41 +0000)
committerhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 2 Jul 2004 17:41:41 +0000 (17:41 +0000)
OGoContentStore/ChangeLog
OGoContentStore/EOQualifier+OCS.h [new file with mode: 0644]
OGoContentStore/EOQualifier+OCS.m [new file with mode: 0644]
OGoContentStore/GNUmakefile
OGoContentStore/OCSChannelManager.m
OGoContentStore/OCSFolder.h
OGoContentStore/OCSFolder.m
OGoContentStore/README

index 8c9836338ea4ecf6bf6ecf197e1eac2d32262c87..c6cb08fa9c264f94150ad8f786ef538cd2e4d670 100644 (file)
@@ -1,3 +1,7 @@
+2004-07-02  Helge Hess  <helge.hess@opengroupware.org>
+
+       * OCSChannelManager.m: added garbage collector for channel pools
+
 2004-06-30  Helge Hess  <helge.hess@opengroupware.org>
 
        * OCSChannelManager.m: implemented pooling
diff --git a/OGoContentStore/EOQualifier+OCS.h b/OGoContentStore/EOQualifier+OCS.h
new file mode 100644 (file)
index 0000000..f1eb13e
--- /dev/null
@@ -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 <EOControl/EOQualifier.h>
+
+@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 (file)
index 0000000..68e6caa
--- /dev/null
@@ -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) */
index 5e9c06518b710f0fd7cc9721ea7c8aa2ddb0c92f..3244dda35f85508a077643539e8fbbecf5cbc5d1 100644 (file)
@@ -25,6 +25,7 @@ libOGoContentStore_HEADER_FILES += \
 libOGoContentStore_OBJC_FILES += \
        NSURL+OCS.m             \
        EOAdaptorChannel+OCS.m  \
+       EOQualifier+OCS.m       \
        \
        OCSContext.m            \
        OCSFieldInfo.m          \
index e51f9c4e652553ca116fc610ec18e8d284b0dca0..3d9a28bfc360dade0f27927e7338a21f2cfe61d1 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "OCSChannelManager.h"
 #include "NSURL+OCS.h"
+#include "EOAdaptorChannel+OCS.h"
 #include <GDLAccess/EOAdaptor.h>
 #include <GDLAccess/EOAdaptorContext.h>
 #include <GDLAccess/EOAdaptorChannel.h>
 
 @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 {
index 7453f399888fcd6593c81eebbd108f1ea7572f5a..627f58ed0a59f2254ef1542c2f508a7c65453f8b 100644 (file)
@@ -82,8 +82,6 @@
 
 - (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q;
 
-- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms;
-
 @end
 
 #endif /* __OGoContentStore_OCSFolder_H__ */
index 3fa18634b12567fb06903ac2564c5359057919e8..f7dfce4b798ff93cebf3e83defa27dc754324e6e 100644 (file)
 #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;
 }
 
index 2fd0d4194ad2ae9b1c80107a8042e26053023010..ef64dd9ca70ac414e4f7c103553a98f545b596d1 100644 (file)
@@ -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