]> err.no Git - sope/blob - sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
added an ivar to WOComponent, minor cleanups
[sope] / sope-gdl1 / PostgreSQL / PostgreSQL72Channel.m
1 /* 
2    PostgreSQL72Channel.m
3
4    Copyright (C) 1999 MDlink online service center GmbH and Helge Hess
5    Copyright (C) 2000-2004 SKYRIX Software AG and Helge Hess
6
7    Author: Helge Hess (helge.hess@opengroupware.org)
8
9    This file is part of the PostgreSQL72 Adaptor Library
10
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Library General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Library General Public License for more details.
20
21    You should have received a copy of the GNU Library General Public
22    License along with this library; see the file COPYING.LIB.
23    If not, write to the Free Software Foundation,
24    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26 // $Id: PostgreSQL72Channel.m 1 2004-08-20 10:38:46Z znek $
27
28 #include <ctype.h>
29 #include <string.h>
30 #include <strings.h>
31 #import "common.h"
32 #import "PostgreSQL72Channel.h"
33 #import "PostgreSQL72Adaptor.h"
34 #import "PostgreSQL72Exception.h"
35 #import "NSString+PostgreSQL72.h"
36 #import "PostgreSQL72Values.h"
37 #import "EOAttribute+PostgreSQL72.h"
38 #include "PGConnection.h"
39
40 #ifndef MIN
41 #  define MIN(x, y) ((x > y) ? y : x)
42 #endif
43
44 #if PG_MAJOR_VERSION >= 6 && PG_MINOR_VERSION > 3
45 #  define NG_HAS_NOTICE_PROCESSOR 1
46 #  define NG_HAS_BINARY_TUPLES    1
47 #  define NG_HAS_FMOD             1
48 #endif
49
50 #if PG_MAJOR_VERSION >= 7 && PG_MINOR_VERSION > 3
51 #  define NG_SET_CLIENT_ENCODING 1
52 #endif
53
54 #define MAX_CHAR_BUF 16384
55
56 @interface PostgreSQL72Channel(Privates)
57 - (void)_resetEvaluationState;
58 @end
59
60 @implementation PostgreSQL72Channel
61
62 #if NG_SET_CLIENT_ENCODING
63 static NSString *PGClientEncoding = @"Latin1";
64 #endif
65 static int      MaxOpenConnectionCount = -1;
66 static BOOL     debugOn     = NO;
67 static NSNull   *null       = nil;
68 static NSNumber *yesObj     = nil;
69 static Class    StringClass = Nil;
70 static Class    MDictClass  = Nil;
71
72 + (void)initialize {
73   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
74   
75   if (null   == nil) null   = [[NSNull null] retain];
76   if (yesObj == nil) yesObj = [[NSNumber numberWithBool:YES] retain];
77   
78   StringClass = [NSString            class];
79   MDictClass  = [NSMutableDictionary class];
80   
81   MaxOpenConnectionCount = [ud integerForKey:@"PGMaxOpenConnectionCount"];
82   if (MaxOpenConnectionCount < 2)
83     MaxOpenConnectionCount = 50;
84   
85   debugOn = [ud boolForKey:@"PGDebugEnabled"];
86 }
87
88 - (id)initWithAdaptorContext:(EOAdaptorContext*)_adaptorContext {
89   if ((self = [super initWithAdaptorContext:_adaptorContext])) {
90     [self setDebugEnabled:debugOn];
91     
92     self->_attributesForTableName = [[MDictClass alloc] initWithCapacity:16];
93     self->_primaryKeysNamesForTableName =
94       [[MDictClass alloc] initWithCapacity:16];
95   }
96   return self;
97 }
98
99 /* collection */
100
101 - (void)dealloc {
102   [self _resetEvaluationState];
103   
104   if ([self isOpen])
105     [self closeChannel];
106   
107   [self->_attributesForTableName       release];
108   [self->_primaryKeysNamesForTableName release];
109   [super dealloc];
110 }
111
112 /* NSCopying methods */
113
114 - (id)copyWithZone:(NSZone *)zone {
115   return [self retain];
116 }
117
118 /* debugging */
119
120 - (void)setDebugEnabled:(BOOL)_flag {
121   self->isDebuggingEnabled = _flag;
122 }
123 - (BOOL)isDebugEnabled {
124   return self->isDebuggingEnabled;
125 }
126
127 - (void)receivedMessage:(NSString *)_message {
128   NSLog(@"%@: message: %@", self, _message);
129 }
130
131 static void _pgMessageProcessor(void *_channel, const char *_msg)
132      __attribute__((unused));
133
134 static void _pgMessageProcessor(void *_channel, const char *_msg) {
135   [(id)_channel receivedMessage:
136        _msg ? [StringClass stringWithCString:_msg] : nil];
137 }
138
139 /* cleanup */
140
141 - (void)_resetResults {
142   [self->resultSet clear];
143   [self->resultSet release];
144   self->resultSet = nil;
145 }
146
147 /* open/close */
148
149 static int openConnectionCount = 0;
150
151 - (BOOL)isOpen {
152   return [self->connection isValid];
153 }
154
155 - (BOOL)openChannel {
156   PostgreSQL72Adaptor *adaptor;
157
158   if ([self->connection isValid]) {
159     NSLog(@"%s: Connection already open !!!", __PRETTY_FUNCTION__);
160     return NO;
161   }
162   
163   adaptor = (PostgreSQL72Adaptor *)[adaptorContext adaptor];
164
165   if (![super openChannel])
166     return NO;
167
168 #if HEAVY_DEBUG
169   NSLog(@"+++++++++ %s: openConnectionCount %d", __PRETTY_FUNCTION__,
170         openConnectionCount);
171 #endif
172     
173   if (openConnectionCount > MaxOpenConnectionCount) {
174     [PostgreSQL72CouldNotOpenChannelException raise:@"NoMoreConnections"
175                                               format:
176                               @"cannot open a additional connection !"];
177     return NO;
178   }
179   
180   self->connection =
181     [[PGConnection alloc] initWithHostName:[adaptor serverName]
182                           port:[(id)[adaptor port] stringValue]
183                           options:[adaptor options]
184                           tty:[adaptor tty] database:[adaptor databaseName]
185                           login:[adaptor loginName]
186                           password:[adaptor loginPassword]];
187   
188   if (![self->connection isValid]) {
189     // could not login ..
190     NSLog(@"WARNING: could not open pgsql channel to %@@%@ host %@:%@",
191           [adaptor loginName],
192           [adaptor databaseName], [adaptor serverName], [adaptor port]);
193     return NO;
194   }
195
196   /* PQstatus */
197   if (![self->connection isConnectionOK]) {
198     NSLog(@"could not open channel to %@@%@",
199           [adaptor databaseName], [adaptor serverName]);
200     [self->connection finish];
201     [self->connection release];
202     self->connection = nil;
203     return NO;
204   }
205   
206   /* set message callback */
207   [self->connection setNoticeProcessor:_pgMessageProcessor context:self];
208   
209   /* set client encoding */
210 #if NG_SET_CLIENT_ENCODING
211   if (![self->connection setClientEncoding:PGClientEncoding]) {
212     NSLog(@"WARNING: could not set client encoding to: '%s'", 
213           PGClientEncoding);
214   }
215 #endif
216   
217   /* log */
218   
219   if (isDebuggingEnabled)
220     NSLog(@"PostgreSQL72 connection established: %@", self->connection);
221
222 #if HEAVY_DEBUG
223   NSLog(@"---------- %s: %@ opens channel count[%d]", __PRETTY_FUNCTION__,
224         self, openConnectionCount);
225 #endif
226   
227   openConnectionCount++;
228
229   if (isDebuggingEnabled) {
230     NSLog(@"PostgreSQL72 channel 0x%08X opened (connection=%@)",
231           (unsigned)self, self->connection);
232   }
233   return YES;
234 }
235
236 - (void)primaryCloseChannel {
237   self->tupleCount = 0;
238   self->fieldCount = 0;
239   self->containsBinaryData = NO;
240     
241   if (self->fieldInfo) {
242     free(self->fieldInfo);
243     self->fieldInfo = NULL;
244   }
245
246   [self _resetResults];
247   
248   [self->cmdStatus release]; self->cmdStatus = nil;
249   [self->cmdTuples release]; self->cmdTuples = nil;
250   
251   if (self->connection) {
252     [self->connection finish];
253 #if HEAVY_DEBUG
254     NSLog(@"---------- %s: %@ close channel count[%d]", __PRETTY_FUNCTION__,
255           self, openConnectionCount);
256 #endif
257     openConnectionCount--;
258     
259     if (isDebuggingEnabled) {
260       fprintf(stderr, 
261               "PostgreSQL72 connection dropped 0x%08X (channel=0x%08X)\n",
262               (unsigned)self->connection, (unsigned)self);
263     }
264     [self->connection release];
265     self->connection = nil;
266   }
267 }
268
269 - (void)closeChannel {
270   [super closeChannel];
271   [self primaryCloseChannel];
272 }
273
274 /* fetching rows */
275
276 - (void)cancelFetch {
277   if (![self isOpen]) {
278     [PostgreSQL72Exception raise:@"ChannelNotOpenException"
279                          format:@"No fetch in progress, connection is not open"
280                            @" (channel=%@)", self];
281   }
282
283 #if HEAVY_DEBUG
284   NSLog(@"canceling fetch (%i tuples remaining).",
285         (self->tupleCount - self->currentTuple));
286 #endif
287   
288   self->tupleCount   = 0;
289   self->currentTuple = 0;
290   self->fieldCount   = 0;
291   self->containsBinaryData = NO;
292     
293   if (self->fieldInfo) {
294     free(self->fieldInfo);
295     self->fieldInfo = NULL;
296   }
297   [self _resetResults];
298   
299   [self->cmdStatus release]; self->cmdStatus = nil;
300   [self->cmdTuples release]; self->cmdTuples = nil;
301   
302   /* new caches which require a constant _attributes argument */
303   if (self->fieldIndices) free(self->fieldIndices); self->fieldIndices = NULL;
304   if (self->fieldKeys)    free(self->fieldKeys);    self->fieldKeys    = NULL;
305   if (self->fieldValues)  free(self->fieldValues);  self->fieldValues  = NULL;
306   
307   [super cancelFetch];
308 }
309
310 - (NSArray *)describeResults {
311   int                 cnt;
312   NSMutableArray      *result    = nil;
313   NSMutableDictionary *usedNames = nil;
314   
315   if (![self isFetchInProgress]) {
316     [PostgreSQL72Exception raise:@"NoFetchInProgress"
317                          format:@"No fetch in progress (channel=%@)", self];
318   }
319   
320   result    = [[NSMutableArray alloc] initWithCapacity:self->fieldCount];
321   usedNames = [[MDictClass     alloc] initWithCapacity:self->fieldCount];
322
323   for (cnt = 0; cnt < self->fieldCount; cnt++) {
324     EOAttribute *attribute  = nil;
325     NSString    *columnName;
326     NSString    *attrName;
327     
328     columnName = 
329       [[StringClass alloc] initWithCString:self->fieldInfo[cnt].name];
330     attrName   = [columnName _pgModelMakeInstanceVarName];
331     
332     if ([[usedNames objectForKey:attrName] boolValue]) {
333       // TODO: move name generation code to different method!
334       int      cnt2 = 0;
335       char     buf[64];
336       NSString *newAttrName = nil;
337       
338       for (cnt2 = 2; cnt2 < 100; cnt2++) {
339         NSString *s;
340         
341         sprintf(buf, "%i", cnt2);
342         
343         s = [[StringClass alloc] initWithCString:buf];
344         newAttrName = [attrName stringByAppendingString:s];
345         [s release]; s= nil;
346         
347         if (![[usedNames objectForKey:newAttrName] boolValue]) {
348           attrName = newAttrName;
349           break;
350         }
351       }
352     }
353     [usedNames setObject:yesObj forKey:attrName];
354
355     attribute = [[EOAttribute alloc] init];
356     [attribute setName:attrName];
357     [attribute setColumnName:columnName];
358     
359     //NSLog(@"column: %@", columnName);
360     
361     [attribute loadValueClassAndTypeUsingPostgreSQLType:
362                  self->fieldInfo[cnt].type
363                size:self->fieldInfo[cnt].size
364                modification:self->fieldInfo[cnt].modification
365                binary:self->containsBinaryData];
366     
367     [result addObject:attribute];
368     
369     [columnName release]; columnName = nil;
370     [attribute  release]; attribute = nil;
371   }
372
373   [usedNames release];
374   usedNames = nil;
375   
376   return [result autorelease];
377 }
378
379 - (void)_fillFieldNamesForAttributes:(NSArray *)_attributes 
380   count:(unsigned)attrCount
381 {
382   // Note: this optimization requires that the "_attributes" array does
383   //       note change between invocations!
384   // TODO: should add a sanity check for that!
385   NSMutableArray *fieldNames;
386   unsigned       nFields, i;
387   unsigned       cnt;
388   
389   if (self->fieldIndices)
390     return;
391   
392   self->fieldIndices = calloc(attrCount + 2, sizeof(int));
393   
394   // TODO: we could probably cache the field-name array for much more speed !
395   fieldNames = [[NSMutableArray alloc] initWithCapacity:32];
396   nFields    = [self->resultSet fieldCount];
397   for (i = 0; i < nFields; i++)
398     [fieldNames addObject:[self->resultSet fieldNameAtIndex:i]];
399   
400   for (cnt = 0; cnt < attrCount; cnt++) {
401     EOAttribute *attribute;
402     
403     attribute = [_attributes objectAtIndex:cnt];
404 #if GDL_USE_PQFNUMBER_INDEX
405     self->fieldIndices[cnt] = 
406       [self->resultSet indexOfFieldNamed:[attribute columnName]];
407 #else
408     self->fieldIndices[cnt] = 
409       [fieldNames indexOfObject:[attribute columnName]];
410 #endif
411     
412     if (self->fieldIndices[cnt] == NSNotFound) {
413       [PostgreSQL72Exception raiseWithFormat:
414                                @"attribute %@ not covered by query",
415                              attribute];
416     }
417     [fieldNames replaceObjectAtIndex:self->fieldIndices[cnt] withObject:null];
418   }
419   [fieldNames release]; fieldNames = nil;
420 }
421
422 - (NSMutableDictionary *)primaryFetchAttributes:(NSArray *)_attributes
423   withZone:(NSZone *)_zone
424 {
425   NSMutableDictionary *row;
426   unsigned     attrCount;
427   int          *indices;
428   unsigned     cnt, fieldDictCount;
429   
430   if (self->currentTuple == self->tupleCount) {
431     if (self->resultSet != nil) [self cancelFetch];
432     return nil;
433   }
434   
435   attrCount = [_attributes count];
436   [self _fillFieldNamesForAttributes:_attributes count:attrCount];
437   indices = self->fieldIndices;
438   
439   if (self->fieldKeys == NULL)
440     self->fieldKeys = calloc(attrCount + 1, sizeof(NSString *));
441   if (self->fieldValues == NULL)
442     self->fieldValues = calloc(attrCount + 1, sizeof(id));
443   fieldDictCount = 0;
444   
445   for (cnt = 0; cnt < attrCount; cnt++) {
446     EOAttribute *attribute;
447     NSString    *attrName;
448     id          value      = nil;
449     Class       valueClass = Nil;
450     const char  *pvalue;
451     int         vallen;
452
453     attribute = [_attributes objectAtIndex:cnt];
454     attrName  = [attribute name];
455
456     if ([self->resultSet isNullTuple:self->currentTuple atIndex:indices[cnt]]){
457       self->fieldKeys[fieldDictCount]   = attrName;
458       self->fieldValues[fieldDictCount] = null;
459       fieldDictCount++;
460       continue;
461     }
462     
463     valueClass = NSClassFromString([attribute valueClassName]);
464     if (valueClass == Nil) {
465       NSLog(@"ERROR(%s): %@: got no value class for column:\n"
466             @"  attribute=%@\n  type=%@",
467             __PRETTY_FUNCTION__, self,
468             attrName, [attribute externalType]);
469       continue;
470     }
471       
472     pvalue = [self->resultSet rawValueOfTuple:self->currentTuple 
473                               atIndex:indices[cnt]];
474     vallen = [self->resultSet lengthOfTuple:self->currentTuple 
475                               atIndex:indices[cnt]];
476     
477     if (self->containsBinaryData) {
478         // pvalue is stored in internal representation
479
480         value = [valueClass valueFromBytes:pvalue length:vallen
481                             postgreSQLType:[attribute externalType]
482                             attribute:attribute
483                             adaptorChannel:self];
484     }
485     else {
486         // pvalue is ascii string
487         
488         value = [valueClass valueFromCString:pvalue length:vallen
489                             postgreSQLType:[attribute externalType]
490                             attribute:attribute
491                             adaptorChannel:self];
492     }
493     if (value == nil) {
494       NSLog(@"ERROR(%s): %@: got no value for column:\n"
495             @"  attribute=%@\n  valueClass=%@\n  type=%@",
496             __PRETTY_FUNCTION__, self,
497             attrName, NSStringFromClass(valueClass), 
498             [attribute externalType]);
499       continue;
500     }
501
502     /* add to dictionary */
503     self->fieldKeys[fieldDictCount]   = attrName;
504     self->fieldValues[fieldDictCount] = value;
505     fieldDictCount++;
506   }
507   
508   self->currentTuple++;
509
510   // TODO: we would need to have a copy on write dict here, ideally with
511   //       the keys being reused for each fetch-loop
512   row = [[MDictClass alloc] initWithObjects:self->fieldValues
513                             forKeys:self->fieldKeys
514                             count:fieldDictCount];
515   return [row autorelease];
516 }
517
518 /* sending sql to server */
519
520 - (void)_resetEvaluationState {
521   self->isFetchInProgress = NO;
522   self->tupleCount   = 0;
523   self->fieldCount   = 0;
524   self->currentTuple = 0;
525   self->containsBinaryData = NO;
526   if (self->fieldInfo) {
527     free(self->fieldInfo);
528     self->fieldInfo = NULL;
529   }
530   
531   /* new caches which require a constant _attributes argument */
532   if (self->fieldIndices) free(self->fieldIndices); self->fieldIndices = NULL;
533   if (self->fieldKeys)    free(self->fieldKeys);    self->fieldKeys    = NULL;
534   if (self->fieldValues)  free(self->fieldValues);  self->fieldValues  = NULL;
535 }
536
537 - (NSException *)_processEvaluationTuplesOKForExpression:(NSString *)_sql {
538   int i;
539   
540   self->isFetchInProgress = YES;
541   
542   self->tupleCount         = [self->resultSet tupleCount];
543   self->fieldCount         = [self->resultSet fieldCount];
544   self->containsBinaryData = [self->resultSet containsBinaryTuples];
545   
546   self->fieldInfo = 
547     calloc(self->fieldCount + 1, sizeof(PostgreSQL72FieldInfo));
548   for (i = 0; i < self->fieldCount; i++) {
549     self->fieldInfo[i].name = PQfname(self->resultSet->results, i);
550     self->fieldInfo[i].type = PQftype(self->resultSet->results, i);
551     self->fieldInfo[i].size = [self->resultSet fieldSizeAtIndex:i];
552     self->fieldInfo[i].modification = [self->resultSet modifierAtIndex:i];
553   }
554   
555   self->cmdStatus = [[self->resultSet commandStatus] copy];
556   self->cmdTuples = [[self->resultSet commandTuples] copy];
557   
558   if (delegateRespondsTo.didEvaluateExpression)
559     [delegate adaptorChannel:self didEvaluateExpression:_sql];
560   
561 #if HEAVY_DEBUG
562   NSLog(@"tuples %i fields %i status %@",
563         self->tupleCount, self->fieldCount, self->cmdStatus);
564 #endif
565   return nil;
566 }
567
568 - (NSException *)_handleBadResponseError {
569   NSString *s;
570
571   [self _resetResults];
572
573   s = [NSString stringWithFormat:@"bad pgsql response (channel=%@): %@", 
574                 self, [self->connection errorMessage]];
575   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72BadResponse"
576                                 reason:s userInfo:nil];
577 }
578 - (NSException *)_handleNonFatalEvaluationError {
579   NSString *s;
580   
581   [self _resetResults];
582   
583   s = [NSString stringWithFormat:@"pgsql error (channel=%@): %@",
584                 self, [self->connection errorMessage]];
585   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72Error"
586                                 reason:s userInfo:nil];
587 }
588 - (NSException *)_handleFatalEvaluationError {
589   NSString *s;
590   
591   [self _resetResults];
592   
593   s = [NSString stringWithFormat:@"fatal pgsql error (channel=%@): %@",
594                 self, [self->connection errorMessage]];
595   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72FatalError"
596                                 reason:s userInfo:nil];
597 }
598
599 - (NSException *)evaluateExpressionX:(NSString *)_expression {
600   BOOL result;
601
602   *(&result) = YES;
603
604   if (_expression == nil) {
605     return [NSException exceptionWithName:NSInvalidArgumentException
606                         reason:@"parameter for evaluateExpression: "
607                         @"must not be null"
608                         userInfo:nil];
609   }
610   
611   *(&_expression) = [[_expression mutableCopy] autorelease];
612
613   if (delegateRespondsTo.willEvaluateExpression) {
614     EODelegateResponse response;
615     
616     response = [delegate adaptorChannel:self
617                          willEvaluateExpression:
618                            (NSMutableString *)_expression];
619     
620     if (response == EODelegateRejects) {
621       return [NSException exceptionWithName:@"EODelegateRejects"
622                           reason:@"delegate rejected insert"
623                           userInfo:nil];
624     }
625     if (response == EODelegateOverrides)
626       return nil;
627   }
628   
629   if (![self isOpen]) {
630     return [PostgreSQL72Exception exceptionWithName:@"ChannelNotOpenException"
631                                   reason:
632                                     @"PostgreSQL72 connection is not open"
633                                   userInfo:nil];
634   }
635   if (self->resultSet != nil) {
636     return [PostgreSQL72Exception exceptionWithName:
637                                     @"CommandInProgressException"
638                                   reason:@"an evaluation is in progress"
639                                   userInfo:nil];
640   }
641
642   if (isDebuggingEnabled)
643     NSLog(@"PG0x%08X SQL: %@", (unsigned)self, _expression);
644   
645   [self _resetEvaluationState];
646   
647   self->resultSet = [[self->connection execute:_expression] retain];
648   if (self->resultSet == nil) {
649     return [PostgreSQL72Exception exceptionWithName:@"ExecutionFailed"
650                                   reason:@"the PQexec() failed"
651                                   userInfo:nil];
652   }
653
654   /* process results */
655   
656   switch (PQresultStatus(self->resultSet->results)) {
657     case PGRES_EMPTY_QUERY:
658     case PGRES_COMMAND_OK:
659       [self _resetResults];
660       
661       if (delegateRespondsTo.didEvaluateExpression)
662         [delegate adaptorChannel:self didEvaluateExpression:_expression];
663       return nil;
664       
665     case PGRES_TUPLES_OK:
666       return [self _processEvaluationTuplesOKForExpression:_expression];
667       
668     case PGRES_COPY_OUT:
669     case PGRES_COPY_IN:
670       [self _resetResults];
671       return [PostgreSQL72Exception exceptionWithName:@"UnsupportedOperation"
672                                     reason:@"copy(out|in) not supported"
673                                     userInfo:nil];
674       
675     case PGRES_BAD_RESPONSE:
676       return [self _handleBadResponseError];
677     case PGRES_NONFATAL_ERROR:
678       return [self _handleNonFatalEvaluationError];
679     case PGRES_FATAL_ERROR:
680       return [self _handleFatalEvaluationError];
681       
682     default:
683       return [NSException exceptionWithName:@"PostgreSQLEvalFailed"
684                           reason:@"generic reason"
685                           userInfo:nil];
686   }
687 }
688 - (BOOL)evaluateExpression:(NSString *)_sql {
689   NSException *e;
690   NSString *n;
691   
692   if ((e = [self evaluateExpressionX:_sql]) == nil)
693     return YES;
694   
695   /* for compatibility with non-X methods, translate some errors to a bool */
696   n = [e name];
697   if ([n isEqualToString:@"EOEvaluationError"])
698     return NO;
699   if ([n isEqualToString:@"EODelegateRejects"])
700     return NO;
701   
702   [e raise];
703   return NO;
704 }
705
706 /* description */
707
708 - (NSString *)description {
709   NSMutableString *ms;
710
711   ms = [NSMutableString stringWithCapacity:128];
712   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
713   if (self->connection)
714     [ms appendFormat:@" connection=%@", self->connection];
715   else
716     [ms appendString:@" not-connected"];
717   [ms appendString:@">"];
718   return ms;
719 }
720
721 @end /* PostgreSQL72Channel */
722
723 @implementation PostgreSQL72Channel(PrimaryKeyGeneration)
724
725 - (NSDictionary *)primaryKeyForNewRowWithEntity:(EOEntity *)_entity {
726   NSArray             *pkeys;
727   PostgreSQL72Adaptor *adaptor;
728   NSString            *seqName, *seq;
729   NSDictionary        *pkey;
730
731   pkeys   = [_entity primaryKeyAttributeNames];
732   adaptor = (id)[[self adaptorContext] adaptor];
733   seqName = [adaptor primaryKeySequenceName];
734   pkey    = nil;
735   seq     = nil;
736
737   seq = [seqName length] > 0
738     ? [StringClass stringWithFormat:@"SELECT NEXTVAL ('%@')", seqName]
739     : [adaptor newKeyExpression];
740   
741   // TODO: since we use evaluateExpressionX, we should not see exceptions?
742   NS_DURING {
743     if ([self evaluateExpressionX:seq] == nil) {
744       id key = nil;
745
746       if (self->tupleCount > 0) {
747         if ([self->resultSet isNullTuple:0 atIndex:0])
748           key = [null retain];
749         else {
750           const char *pvalue;
751           int         vallen;
752           
753           if (self->containsBinaryData) {
754 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
755             NSLog(@"%s: binary data not implemented!", __PRETTY_FUNCTION__);
756 #else
757             [self notImplemented:_cmd];
758 #endif
759           }
760           
761           pvalue = [self->resultSet rawValueOfTuple:0 atIndex:0];
762           vallen = [self->resultSet lengthOfTuple:0   atIndex:0];
763           
764           if (pvalue)
765             key = [[NSNumber alloc] initWithInt:atoi(pvalue)];
766         }
767       }
768       [self cancelFetch];
769
770       if (key) {
771         pkey = [NSDictionary dictionaryWithObject:key
772                              forKey:[pkeys objectAtIndex:0]];
773         [key release]; key = nil;
774       }
775     }
776   }
777   NS_HANDLER
778     pkey = nil;
779   NS_ENDHANDLER;
780
781   return pkey;
782 }
783
784 @end /* PostgreSQL72Channel(PrimaryKeyGeneration) */
785
786 void __link_PostgreSQL72Channel() {
787   // used to force linking of object file
788   __link_PostgreSQL72Channel();
789 }