]> err.no Git - sope/blob - sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
fixed a Tiger warning
[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:(BOOL)_beautifyNames {
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   = _beautifyNames
331       ? [columnName _pgModelMakeInstanceVarName]
332       : columnName;
333     
334     if ([[usedNames objectForKey:attrName] boolValue]) {
335       // TODO: move name generation code to different method!
336       int      cnt2 = 0;
337       char     buf[64];
338       NSString *newAttrName = nil;
339       
340       for (cnt2 = 2; cnt2 < 100; cnt2++) {
341         NSString *s;
342         
343         sprintf(buf, "%i", cnt2);
344         
345         s = [[StringClass alloc] initWithCString:buf];
346         newAttrName = [attrName stringByAppendingString:s];
347         [s release]; s= nil;
348         
349         if (![[usedNames objectForKey:newAttrName] boolValue]) {
350           attrName = newAttrName;
351           break;
352         }
353       }
354     }
355     [usedNames setObject:yesObj forKey:attrName];
356
357     attribute = [[EOAttribute alloc] init];
358     [attribute setName:attrName];
359     [attribute setColumnName:columnName];
360     
361     //NSLog(@"column: %@", columnName);
362     
363     [attribute loadValueClassAndTypeUsingPostgreSQLType:
364                  self->fieldInfo[cnt].type
365                size:self->fieldInfo[cnt].size
366                modification:self->fieldInfo[cnt].modification
367                binary:self->containsBinaryData];
368     
369     [result addObject:attribute];
370     
371     [columnName release]; columnName = nil;
372     [attribute  release]; attribute = nil;
373   }
374
375   [usedNames release];
376   usedNames = nil;
377   
378   return [result autorelease];
379 }
380
381 - (void)_fillFieldNamesForAttributes:(NSArray *)_attributes 
382   count:(unsigned)attrCount
383 {
384   // Note: this optimization requires that the "_attributes" array does
385   //       note change between invocations!
386   // TODO: should add a sanity check for that!
387   NSMutableArray *fieldNames;
388   unsigned       nFields, i;
389   unsigned       cnt;
390   
391   if (self->fieldIndices)
392     return;
393   
394   self->fieldIndices = calloc(attrCount + 2, sizeof(int));
395   
396   // TODO: we could probably cache the field-name array for much more speed !
397   fieldNames = [[NSMutableArray alloc] initWithCapacity:32];
398   nFields    = [self->resultSet fieldCount];
399   for (i = 0; i < nFields; i++)
400     [fieldNames addObject:[self->resultSet fieldNameAtIndex:i]];
401   
402   for (cnt = 0; cnt < attrCount; cnt++) {
403     EOAttribute *attribute;
404     
405     attribute = [_attributes objectAtIndex:cnt];
406 #if GDL_USE_PQFNUMBER_INDEX
407     self->fieldIndices[cnt] = 
408       [self->resultSet indexOfFieldNamed:[attribute columnName]];
409 #else
410     self->fieldIndices[cnt] = 
411       [fieldNames indexOfObject:[attribute columnName]];
412 #endif
413     
414     if (self->fieldIndices[cnt] == NSNotFound) {
415       [PostgreSQL72Exception raiseWithFormat:
416                                @"attribute %@ not covered by query",
417                              attribute];
418     }
419     [fieldNames replaceObjectAtIndex:self->fieldIndices[cnt] withObject:null];
420   }
421   [fieldNames release]; fieldNames = nil;
422 }
423
424 - (NSMutableDictionary *)primaryFetchAttributes:(NSArray *)_attributes
425   withZone:(NSZone *)_zone
426 {
427   NSMutableDictionary *row;
428   unsigned     attrCount;
429   int          *indices;
430   unsigned     cnt, fieldDictCount;
431   
432   if (self->currentTuple == self->tupleCount) {
433     if (self->resultSet != nil) [self cancelFetch];
434     return nil;
435   }
436   
437   attrCount = [_attributes count];
438   [self _fillFieldNamesForAttributes:_attributes count:attrCount];
439   indices = self->fieldIndices;
440   
441   if (self->fieldKeys == NULL)
442     self->fieldKeys = calloc(attrCount + 1, sizeof(NSString *));
443   if (self->fieldValues == NULL)
444     self->fieldValues = calloc(attrCount + 1, sizeof(id));
445   fieldDictCount = 0;
446   
447   for (cnt = 0; cnt < attrCount; cnt++) {
448     EOAttribute *attribute;
449     NSString    *attrName;
450     id          value      = nil;
451     Class       valueClass = Nil;
452     const char  *pvalue;
453     int         vallen;
454
455     attribute = [_attributes objectAtIndex:cnt];
456     attrName  = [attribute name];
457
458     if ([self->resultSet isNullTuple:self->currentTuple atIndex:indices[cnt]]){
459       self->fieldKeys[fieldDictCount]   = attrName;
460       self->fieldValues[fieldDictCount] = null;
461       fieldDictCount++;
462       continue;
463     }
464     
465     valueClass = NSClassFromString([attribute valueClassName]);
466     if (valueClass == Nil) {
467       NSLog(@"ERROR(%s): %@: got no value class for column:\n"
468             @"  attribute=%@\n  type=%@",
469             __PRETTY_FUNCTION__, self,
470             attrName, [attribute externalType]);
471       continue;
472     }
473       
474     pvalue = [self->resultSet rawValueOfTuple:self->currentTuple 
475                               atIndex:indices[cnt]];
476     vallen = [self->resultSet lengthOfTuple:self->currentTuple 
477                               atIndex:indices[cnt]];
478     
479     if (self->containsBinaryData) {
480         // pvalue is stored in internal representation
481
482         value = [valueClass valueFromBytes:pvalue length:vallen
483                             postgreSQLType:[attribute externalType]
484                             attribute:attribute
485                             adaptorChannel:self];
486     }
487     else {
488         // pvalue is ascii string
489         
490         value = [valueClass valueFromCString:pvalue length:vallen
491                             postgreSQLType:[attribute externalType]
492                             attribute:attribute
493                             adaptorChannel:self];
494     }
495     if (value == nil) {
496       NSLog(@"ERROR(%s): %@: got no value for column:\n"
497             @"  attribute=%@\n  valueClass=%@\n  type=%@",
498             __PRETTY_FUNCTION__, self,
499             attrName, NSStringFromClass(valueClass), 
500             [attribute externalType]);
501       continue;
502     }
503
504     /* add to dictionary */
505     self->fieldKeys[fieldDictCount]   = attrName;
506     self->fieldValues[fieldDictCount] = value;
507     fieldDictCount++;
508   }
509   
510   self->currentTuple++;
511
512   // TODO: we would need to have a copy on write dict here, ideally with
513   //       the keys being reused for each fetch-loop
514   row = [[MDictClass alloc] initWithObjects:self->fieldValues
515                             forKeys:self->fieldKeys
516                             count:fieldDictCount];
517   return [row autorelease];
518 }
519
520 /* sending sql to server */
521
522 - (void)_resetEvaluationState {
523   self->isFetchInProgress = NO;
524   self->tupleCount   = 0;
525   self->fieldCount   = 0;
526   self->currentTuple = 0;
527   self->containsBinaryData = NO;
528   if (self->fieldInfo) {
529     free(self->fieldInfo);
530     self->fieldInfo = NULL;
531   }
532   
533   /* new caches which require a constant _attributes argument */
534   if (self->fieldIndices) free(self->fieldIndices); self->fieldIndices = NULL;
535   if (self->fieldKeys)    free(self->fieldKeys);    self->fieldKeys    = NULL;
536   if (self->fieldValues)  free(self->fieldValues);  self->fieldValues  = NULL;
537 }
538
539 - (NSException *)_processEvaluationTuplesOKForExpression:(NSString *)_sql {
540   int i;
541   
542   self->isFetchInProgress = YES;
543   
544   self->tupleCount         = [self->resultSet tupleCount];
545   self->fieldCount         = [self->resultSet fieldCount];
546   self->containsBinaryData = [self->resultSet containsBinaryTuples];
547   
548   self->fieldInfo = 
549     calloc(self->fieldCount + 1, sizeof(PostgreSQL72FieldInfo));
550   for (i = 0; i < self->fieldCount; i++) {
551     self->fieldInfo[i].name = PQfname(self->resultSet->results, i);
552     self->fieldInfo[i].type = PQftype(self->resultSet->results, i);
553     self->fieldInfo[i].size = [self->resultSet fieldSizeAtIndex:i];
554     self->fieldInfo[i].modification = [self->resultSet modifierAtIndex:i];
555   }
556   
557   self->cmdStatus = [[self->resultSet commandStatus] copy];
558   self->cmdTuples = [[self->resultSet commandTuples] copy];
559   
560   if (delegateRespondsTo.didEvaluateExpression)
561     [delegate adaptorChannel:self didEvaluateExpression:_sql];
562   
563 #if HEAVY_DEBUG
564   NSLog(@"tuples %i fields %i status %@",
565         self->tupleCount, self->fieldCount, self->cmdStatus);
566 #endif
567   return nil;
568 }
569
570 - (NSException *)_handleBadResponseError {
571   NSString *s;
572
573   [self _resetResults];
574
575   s = [NSString stringWithFormat:@"bad pgsql response (channel=%@): %@", 
576                 self, [self->connection errorMessage]];
577   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72BadResponse"
578                                 reason:s userInfo:nil];
579 }
580 - (NSException *)_handleNonFatalEvaluationError {
581   NSString *s;
582   
583   [self _resetResults];
584   
585   s = [NSString stringWithFormat:@"pgsql error (channel=%@): %@",
586                 self, [self->connection errorMessage]];
587   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72Error"
588                                 reason:s userInfo:nil];
589 }
590 - (NSException *)_handleFatalEvaluationError {
591   NSString *s;
592   
593   [self _resetResults];
594   
595   s = [NSString stringWithFormat:@"fatal pgsql error (channel=%@): %@",
596                 self, [self->connection errorMessage]];
597   return [PostgreSQL72Exception exceptionWithName:@"PostgreSQL72FatalError"
598                                 reason:s userInfo:nil];
599 }
600
601 - (NSException *)evaluateExpressionX:(NSString *)_expression {
602   BOOL result;
603
604   *(&result) = YES;
605
606   if (_expression == nil) {
607     return [NSException exceptionWithName:NSInvalidArgumentException
608                         reason:@"parameter for evaluateExpression: "
609                         @"must not be null"
610                         userInfo:nil];
611   }
612   
613   *(&_expression) = [[_expression mutableCopy] autorelease];
614
615   if (delegateRespondsTo.willEvaluateExpression) {
616     EODelegateResponse response;
617     
618     response = [delegate adaptorChannel:self
619                          willEvaluateExpression:
620                            (NSMutableString *)_expression];
621     
622     if (response == EODelegateRejects) {
623       return [NSException exceptionWithName:@"EODelegateRejects"
624                           reason:@"delegate rejected insert"
625                           userInfo:nil];
626     }
627     if (response == EODelegateOverrides)
628       return nil;
629   }
630   
631   if (![self isOpen]) {
632     return [PostgreSQL72Exception exceptionWithName:@"ChannelNotOpenException"
633                                   reason:
634                                     @"PostgreSQL72 connection is not open"
635                                   userInfo:nil];
636   }
637   if (self->resultSet != nil) {
638     return [PostgreSQL72Exception exceptionWithName:
639                                     @"CommandInProgressException"
640                                   reason:@"an evaluation is in progress"
641                                   userInfo:nil];
642   }
643
644   if (isDebuggingEnabled)
645     NSLog(@"PG0x%08X SQL: %@", (unsigned)self, _expression);
646   
647   [self _resetEvaluationState];
648   
649   self->resultSet = [[self->connection execute:_expression] retain];
650   if (self->resultSet == nil) {
651     return [PostgreSQL72Exception exceptionWithName:@"ExecutionFailed"
652                                   reason:@"the PQexec() failed"
653                                   userInfo:nil];
654   }
655
656   /* process results */
657   
658   switch (PQresultStatus(self->resultSet->results)) {
659     case PGRES_EMPTY_QUERY:
660     case PGRES_COMMAND_OK:
661       [self _resetResults];
662       
663       if (delegateRespondsTo.didEvaluateExpression)
664         [delegate adaptorChannel:self didEvaluateExpression:_expression];
665       return nil;
666       
667     case PGRES_TUPLES_OK:
668       return [self _processEvaluationTuplesOKForExpression:_expression];
669       
670     case PGRES_COPY_OUT:
671     case PGRES_COPY_IN:
672       [self _resetResults];
673       return [PostgreSQL72Exception exceptionWithName:@"UnsupportedOperation"
674                                     reason:@"copy(out|in) not supported"
675                                     userInfo:nil];
676       
677     case PGRES_BAD_RESPONSE:
678       return [self _handleBadResponseError];
679     case PGRES_NONFATAL_ERROR:
680       return [self _handleNonFatalEvaluationError];
681     case PGRES_FATAL_ERROR:
682       return [self _handleFatalEvaluationError];
683       
684     default:
685       return [NSException exceptionWithName:@"PostgreSQLEvalFailed"
686                           reason:@"generic reason"
687                           userInfo:nil];
688   }
689 }
690 - (BOOL)evaluateExpression:(NSString *)_sql {
691   NSException *e;
692   NSString *n;
693   
694   if ((e = [self evaluateExpressionX:_sql]) == nil)
695     return YES;
696   
697   /* for compatibility with non-X methods, translate some errors to a bool */
698   n = [e name];
699   if ([n isEqualToString:@"EOEvaluationError"])
700     return NO;
701   if ([n isEqualToString:@"EODelegateRejects"])
702     return NO;
703   
704   [e raise];
705   return NO;
706 }
707
708 /* description */
709
710 - (NSString *)description {
711   NSMutableString *ms;
712
713   ms = [NSMutableString stringWithCapacity:128];
714   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
715   if (self->connection)
716     [ms appendFormat:@" connection=%@", self->connection];
717   else
718     [ms appendString:@" not-connected"];
719   [ms appendString:@">"];
720   return ms;
721 }
722
723 @end /* PostgreSQL72Channel */
724
725 @implementation PostgreSQL72Channel(PrimaryKeyGeneration)
726
727 - (NSDictionary *)primaryKeyForNewRowWithEntity:(EOEntity *)_entity {
728   NSArray             *pkeys;
729   PostgreSQL72Adaptor *adaptor;
730   NSString            *seqName, *seq;
731   NSDictionary        *pkey;
732
733   pkeys   = [_entity primaryKeyAttributeNames];
734   adaptor = (id)[[self adaptorContext] adaptor];
735   seqName = [adaptor primaryKeySequenceName];
736   pkey    = nil;
737   seq     = nil;
738
739   seq = [seqName length] > 0
740     ? [StringClass stringWithFormat:@"SELECT NEXTVAL ('%@')", seqName]
741     : [adaptor newKeyExpression];
742   
743   // TODO: since we use evaluateExpressionX, we should not see exceptions?
744   NS_DURING {
745     if ([self evaluateExpressionX:seq] == nil) {
746       id key = nil;
747
748       if (self->tupleCount > 0) {
749         if ([self->resultSet isNullTuple:0 atIndex:0])
750           key = [null retain];
751         else {
752           const char *pvalue;
753           int         vallen;
754           
755           if (self->containsBinaryData) {
756 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
757             NSLog(@"%s: binary data not implemented!", __PRETTY_FUNCTION__);
758 #else
759             [self notImplemented:_cmd];
760 #endif
761           }
762           
763           pvalue = [self->resultSet rawValueOfTuple:0 atIndex:0];
764           vallen = [self->resultSet lengthOfTuple:0   atIndex:0];
765           
766           if (pvalue)
767             key = [[NSNumber alloc] initWithInt:atoi(pvalue)];
768         }
769       }
770       [self cancelFetch];
771
772       if (key) {
773         pkey = [NSDictionary dictionaryWithObject:key
774                              forKey:[pkeys objectAtIndex:0]];
775         [key release]; key = nil;
776       }
777     }
778   }
779   NS_HANDLER
780     pkey = nil;
781   NS_ENDHANDLER;
782
783   return pkey;
784 }
785
786 @end /* PostgreSQL72Channel(PrimaryKeyGeneration) */
787
788 void __link_PostgreSQL72Channel() {
789   // used to force linking of object file
790   __link_PostgreSQL72Channel();
791 }