4 Copyright (C) 1999 MDlink online service center GmbH and Helge Hess
6 Author: Helge Hess (helge.hess@mdlink.de)
8 This file is part of the FB Adaptor Library
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this library; see the file COPYING.LIB.
22 If not, write to the Free Software Foundation,
23 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 // $Id: FBChannel.m 1 2004-08-20 10:38:46Z znek $
33 #include "FBBlobHandle.h"
34 #include <NGExtensions/NSString+misc.h>
36 #include <FBCAccess/FBCDigestPassword.h>
37 #include <GDLAccess/EORecordDictionary.h>
38 #import <EOControl/EOSortOrdering.h>
40 //#define BLOB_DEBUG 1
42 @implementation FrontBaseChannel
44 static EONull *null = nil;
47 if (null == NULL) null = [[EONull null] retain];
51 extern __declspec(import) void fbcInitialize(void);
54 - (id)initWithAdaptorContext:(EOAdaptorContext*)_adaptorContext {
55 static BOOL didInit = NO;
66 if ((self = [super initWithAdaptorContext:_adaptorContext])) {
67 [self setDebugEnabled:[[NSUserDefaults standardUserDefaults]
68 boolForKey:@"FBDebugEnabled"]];
69 sqlLogFile = [[[NSUserDefaults standardUserDefaults]
70 stringForKey:@"FBLogFile"]
71 copyWithZone:[self zone]];
72 self->_primaryKeysNamesForTableName = [[NSMutableDictionary alloc] init];
73 self->_attributesForTableName = [[NSMutableDictionary alloc] init];
78 #if !LIB_FOUNDATION_BOEHM_GC
84 RELEASE(self->sqlLogFile);
85 RELEASE(self->_primaryKeysNamesForTableName);
86 RELEASE(self->_attributesForTableName);
93 - (id)copyWithZone:(NSZone *)zone {
94 return RETAIN(self);// copy is needed during creation of NSNotification object
99 - (void)setDebugEnabled:(BOOL)_flag {
100 self->isDebuggingEnabled = _flag;
102 - (BOOL)isDebugEnabled {
103 return self->isDebuggingEnabled;
109 return self->fbdc != NULL ? YES : NO;
112 - (BOOL)openChannel {
113 FrontBase2Adaptor *adaptor;
116 NSString *txIsoLevel;
119 const char *password;
122 // [self setDebugEnabled:YES];
124 adaptor = (FrontBase2Adaptor *)[[self adaptorContext] adaptor];
126 if (![super openChannel])
129 /* digest the database password */
131 if ((password = [[adaptor databasePassword] cString])) {
132 if (fbcDigestPassword("_SYSTEM", (char *)password, pwdDigest) == NULL) {
133 NSLog(@"%@: Couldn't digest password !", self);
140 /* connect to database */
142 if (isDebuggingEnabled)
143 NSLog(@"Open fb-channel[%p]: db=%@, server=%@", self,
144 [adaptor databaseName], [adaptor serverName]);
147 fbcdcConnectToDatabase((char *)[[adaptor databaseName] cString],
148 (char *)[[adaptor serverName] cString],
150 if (self->fbdc == NULL) {
151 if (isDebuggingEnabled) {
152 NSLog(@"FrontBase channel 0x%08X (db=%@, server=%@, digest=%s) "
153 @"could not be opened: %s ..", self,
154 [adaptor databaseName], [adaptor serverName],
155 pwdDigest /* ? "yes" : "no"*/,
156 fbcdcClassErrorMessage());
161 fbcdcSetRollbackAfterError(self->fbdc, True);
163 /* digest the user password */
165 if ((password = [[adaptor loginPassword] cString])) {
166 if (fbcDigestPassword((char *)[[adaptor loginName] cString],
167 (char *)password, pwdDigest) == NULL) {
168 NSLog(@"%@: Couldn't digest password !", self);
175 if (!(password = [[adaptor loginPassword] cString]))
181 md = fbcdcCreateSession(self->fbdc,
182 (char *)[[NSString stringWithFormat:
183 @"GDL<0x%08X>", self]
185 (char *)[[adaptor loginName] cString],
188 (char *)[NSUserName() cString]);
190 if (isDebuggingEnabled) {
191 NSLog(@"FrontBase session (channel=0x%08X) couldn't be created, "
192 @" login=%@ password=%s user=%@: %s.",
194 [adaptor loginName], [adaptor loginPassword],
196 fbcdcClassErrorMessage());
198 fbcdcClose(self->fbdc); self->fbdc = NULL;
202 if (fbcmdErrorCount(md) > 0) {
203 if (isDebuggingEnabled) {
204 FBCErrorMetaData *emd;
207 emd = fbcdcErrorMetaData(self->fbdc, md);
209 NSLog(@"FrontBase session (channel=0x%08X) couldn't be created:", self);
211 for (i = 0, count = fbcemdErrorCount(emd); i < count; i++) {
213 const char *errorKind;
216 code = fbcemdErrorCodeAtIndex(emd, i);
217 errorKind = fbcemdErrorKindAtIndex(emd, i);
218 emsg = fbcemdErrorMessageAtIndex(emd, i);
220 NSLog(@" code=%i", code);
221 NSLog(@" kind=%s", errorKind);
222 NSLog(@" msdg=%s", emsg);
224 free((void *)errorKind);
228 fbcemdRelease(emd); emd = NULL;
230 fbcdcClose(self->fbdc); self->fbdc = NULL;
231 fbcmdRelease(md); md = NULL;
235 fbcmdRelease(md); md = NULL;
239 /* apply session level setting (locking & tx levels) */
241 if ((txIsoLevel = [adaptor transactionIsolationLevel]) == nil)
242 txIsoLevel = @"READ COMMITTED";
243 if ((locking = [adaptor lockingDiscipline]) == nil)
244 locking = @"OPTIMISTIC";
246 expr = [NSString stringWithFormat:
247 @"SET TRANSACTION ISOLATION LEVEL %s, LOCKING %s",
248 [txIsoLevel cString], [locking cString]];
249 if (![self evaluateExpression:expr]) {
250 if (isDebuggingEnabled)
251 NSLog(@"%@: couldn't apply tx iso level '%@' and locking '%@'..",
252 self, txIsoLevel, locking);
255 #if LIB_FOUNDATION_BOEHM_GC
256 [GarbageCollector registerForFinalizationObserver:self
257 selector:@selector(_adaptorWillFinalize:)
258 object:[[self adaptorContext] adaptor]];
261 if (isDebuggingEnabled)
262 NSLog(@"FrontBase channel 0x%08X opened ..", self);
267 - (void)primaryCloseChannel {
268 /* release all resources */
271 if (isDebuggingEnabled) {
274 ad = [[self adaptorContext] adaptor];
275 NSLog(@"Close fb-channel[%p]: db=%@, server=%@", self,
276 ad, [ad serverName]);
279 fbcdcClose(self->fbdc);
283 RELEASE(self->selectedAttributes); self->selectedAttributes = nil;
286 - (void)closeChannel {
287 [super closeChannel];
288 [self primaryCloseChannel];
293 - (void)cancelFetch {
294 if (![self isOpen]) {
295 [FrontBaseException raise:@"ChannelNotOpenException"
296 format:@"No fetch in progress, fb connection is not"
297 @" open (channel=%@)", self];
300 if (self->fetchHandle) {
302 if ((md = fbcdcCancelFetch(self->fbdc, self->fetchHandle))) {
303 fbcmdRelease(md); md = NULL;
305 self->fetchHandle = NULL;
308 if (self->datatypeCodes) {
309 free(self->datatypeCodes);
310 self->datatypeCodes = NULL;
313 if (self->rowHandler) {
314 fbcrhRelease(self->rowHandler);
315 self->rowHandler = NULL;
316 self->rawRows = NULL;
319 NSAssert(self->rawRows == NULL, @"raw rows set, but no row handler ! ..");
322 if (self->cmdMetaData) {
323 fbcmdRelease(self->cmdMetaData);
324 self->cmdMetaData = NULL;
327 NSAssert(self->rowHandler == NULL, @"row handler still set ..");
328 NSAssert(self->rawRows == NULL, @"raw row still set ..");
332 self->currentRow = 0;
333 self->numberOfColumns = 0;
334 RELEASE(self->selectedAttributes); self->selectedAttributes = nil;
337 - (NSArray *)describeResults {
339 NSMutableArray *result = nil;
340 NSMutableDictionary *usedNames = nil;
341 NSNumber *yesObj = [NSNumber numberWithBool:YES];
343 if (![self isFetchInProgress]) {
344 [FrontBaseException raise:@"NoFetchInProgress"
345 format:@"No fetch in progress (channel=%@)", self];
348 NSAssert(self->cmdMetaData, @"no cmd-meta-data ..");
351 [[NSMutableArray alloc] initWithCapacity:self->numberOfColumns + 1];
353 [[NSMutableDictionary alloc] initWithCapacity:self->numberOfColumns + 1];
355 for (cnt = 0; cnt < self->numberOfColumns; cnt++) {
356 const FBCColumnMetaData *cmd;
357 EOAttribute *attribute = nil;
358 NSString *columnName = nil;
359 NSString *attrName = nil;
361 cmd = fbcmdColumnMetaDataAtIndex(self->cmdMetaData, cnt);
366 if ((s = fbccmdLabelName(cmd)))
367 columnName = [NSString stringWithCString:s];
368 else if ((s = fbccmdColumnName(cmd)))
369 columnName = [NSString stringWithCString:s];
372 if ([columnName length] == 0) {
373 columnName = [NSString stringWithFormat:@"column%i", cnt];
374 attrName = [NSString stringWithFormat:@"column%i", cnt];
377 attrName = [columnName _sybModelMakeInstanceVarName];
379 if ([[usedNames objectForKey:attrName] boolValue]) {
382 NSString *newAttrName = nil;
384 for (cnt2 = 2; cnt2 < 100; cnt2++) {
385 sprintf(buf, "%i", cnt2);
387 newAttrName = [attrName stringByAppendingString:
388 [NSString stringWithCString:buf]];
390 if (![[usedNames objectForKey:newAttrName] boolValue]) {
391 attrName = newAttrName;
396 [usedNames setObject:yesObj forKey:attrName];
398 attribute = [[EOAttribute alloc] init];
399 [attribute setName:attrName];
400 [attribute setColumnName:columnName];
401 [attribute loadValueClassAndTypeFromFrontBaseType:self->datatypeCodes[cnt]];
402 [result addObject:attribute];
403 RELEASE(attribute); attribute = nil;
409 return AUTORELEASE(result);
417 if (self->rowHandler) {
418 fbcrhRelease(self->rowHandler);
419 self->rowHandler = NULL;
420 self->rawRows = NULL;
423 //NSLog(@"fetching new batch ..");
424 NSAssert(self->fetchHandle, @"missing fetch handle ..");
425 self->rawRows = fbcdcFetch(self->fbdc, [self batchSize], self->fetchHandle);
428 self->rowHandler = fbcrhInitWith(self->rawRows, self->cmdMetaData);
429 self->isFirstInBatch = YES;
430 //NSLog(@"fetched new batch %i rows ..", fbcrhRowCount(self->rowHandler));
433 /* no more fetch data, finish fetch op */
435 self->isFetchInProgress = NO;
442 - (NSMutableDictionary *)primaryFetchAttributes:(NSArray *)_attributes
443 withZone:(NSZone *)_zone
445 NSMutableDictionary *record;
451 if (!self->isFetchInProgress)
454 if (self->rawRows == NULL) {
455 if (![self _nextBatch])
459 if (self->selectedAttributes == nil) {
460 self->selectedAttributes = RETAIN([self describeResults]);
463 if ([_attributes count] < [self->selectedAttributes count])
464 _attributes = self->selectedAttributes;
466 NSAssert(self->rowHandler, @"missing row handler ..");
468 rawRow = self->isFirstInBatch
469 ? fbcrhFirstRow(self->rowHandler)
470 : fbcrhNextRow(self->rowHandler);
472 if (rawRow == NULL) {
473 /* no more rows available in fetch-buffer, get next batch */
474 if (![self _nextBatch])
477 rawRow = self->isFirstInBatch
478 ? fbcrhFirstRow(self->rowHandler)
479 : fbcrhNextRow(self->rowHandler);
481 self->isFirstInBatch = NO;
483 if (rawRow == NULL) {
484 /* no rows were in batch and no new batch could be fetched, so we finished */
486 self->isFetchInProgress = NO;
490 /* deconstruct raw row */
493 id objects[self->numberOfColumns];
494 id keys[self->numberOfColumns];
496 for (i = 0; i < self->numberOfColumns; i++) {
497 EOAttribute *attribute;
498 register void *rawValue;
506 if (self->datatypeCodes[i] < 1)
510 if (!(attribute = [_attributes objectAtIndex:i])) {
511 attribute = [self->selectedAttributes objectAtIndex:i];
515 attrName = [attribute name];
516 valueClass = NSClassFromString([attribute valueClassName]);
519 NSAssert3(valueClass,
520 @"no valueClass for attribute %@ entity %@ name %@ ..",
521 attribute, [attribute entity], [attribute valueClassName]);
525 attrName = [NSString stringWithFormat:@"column%i", i];
527 rawValue = rawRow[i];
528 if (rawValue == NULL) {
532 FBCBlobHandle *handle = NULL;
533 unsigned int len = 4;
535 switch (self->datatypeCodes[i]) {
537 len = sizeof(unsigned char);
541 len = strlen(rawValue);
544 len = strlen(rawValue);
547 NSLog(@"handling date ..");
548 len = strlen(rawValue);
551 NSLog(@"handling timestamp ..");
552 len = strlen(rawValue);
555 len = strlen(rawValue);
557 NSLog(@"handling timestamp with TZ with length=%i ..", len);
561 case FB_SmallInteger:
575 len = sizeof(double);
585 CLOB/BLOB are a bit more tricky, mainly because values of up to a
586 given size are inlined in the fetch result, i.e. no need for a
587 round trip to the server.
589 if ((*(unsigned char *)rawValue) == 0) {
590 /* blob via handle */
592 NSLog(@"blob via handle %s",
593 ((FBCBlobIndirect *)rawValue)->handleAsString);
597 fbcbhInitWithHandle(((FBCBlobIndirect *)rawValue)->handleAsString);
598 len = fbcbhBlobSize(handle);
600 rawValue = fbcdcReadBLOB(self->fbdc, handle);
607 NSLog(@"blob inline");
610 rawValue = blob->blobData;
611 len = blob->blobSize;
614 NSLog(@" size=%d", len);
619 /* make an object value from the data */
622 NSAssert3(valueClass,
623 @"lost valueClass for attribute %@ entity %@ name %@ ..",
624 attribute, [attribute entity], [attribute valueClassName]);
627 value = [valueClass valueFromBytes:rawValue length:len
628 frontBaseType:self->datatypeCodes[i]
630 adaptorChannel:self];
635 @"no value for attribute %@ entity %@ "
636 @"valueClass %@, record %@ ..",
637 attribute, [attribute entity],
638 NSStringFromClass(valueClass), record);
642 /* free BLOB handle if one was allocated */
645 fbcbhRelease(handle);
651 NSAssert2(value, @"no value for attribute %@ entity %@ ..",
652 attribute, [attribute entity]);
656 keys[i] = [attribute name];
659 static Class EORecordDictionaryClass = nil;
661 if (EORecordDictionaryClass == nil)
662 EORecordDictionaryClass = [EORecordDictionary class];
664 record = (id)NSAllocateObject(EORecordDictionaryClass,
665 sizeof(EORecordDictionary) *
666 self->numberOfColumns, nil);
667 record = [record initWithObjects:objects forKeys:keys
668 count:self->numberOfColumns];
675 /* sending sql to server */
677 - (BOOL)evaluateExpression:(NSString *)_expression {
680 static Class NSDateClass = Nil;
685 NSAssert(self->rowHandler == NULL, @"raw handler still available ..");
686 NSAssert(self->rawRows == NULL, @"raw rows still available ..");
687 NSAssert(self->fetchHandle == NULL, @"fetch handle still available ..");
689 if (_expression == nil) {
690 [InvalidArgumentException raise:@"InvalidArgumentException"
691 format:@"parameter for evaluateExpression: "
692 @"must not be null (channel=%@)", self];
695 *(&_expression) = AUTORELEASE([_expression mutableCopy]);
697 if (delegateRespondsTo.willEvaluateExpression) {
698 EODelegateResponse response =
699 [delegate adaptorChannel:self
700 willEvaluateExpression:(NSMutableString *)_expression];
702 if (response == EODelegateRejects)
704 else if (response == EODelegateOverrides)
708 if (isDebuggingEnabled)
709 NSLog(@"SQL[%p]: %@", self, _expression);
712 if (![self isOpen]) {
713 [FrontBaseException raise:@"ChannelNotOpenException"
714 format:@"FrontBase connection is not open (channel=%@)",
717 if (self->cmdMetaData != NULL) {
718 [FrontBaseException raise:@"CommandInProgressException"
719 format:@"command data already set up"
720 @" (channel=%@)", self];
723 _expression = [_expression stringByAppendingString:@";"];
725 if (self->sqlLogFile) {
727 if ((fh = fopen([self->sqlLogFile cString], "a"))) {
728 fprintf(fh, "%s\n", [_expression cString]);
734 /* execute expression */
737 if (NSDateClass == Nil) NSDateClass = [NSDate class];
738 startDate = [NSDateClass date];
741 self->cmdMetaData = fbcdcExecuteDirectSQL(self->fbdc,
742 (char *)[_expression cString]);
743 if (self->cmdMetaData == NULL) {
744 NSLog(@"%@: could not execute SQL: %@", self, _expression);
748 /* check for errors */
750 if (fbcmdErrorCount(self->cmdMetaData) > 0) {
751 FBCErrorMetaData *emd;
756 emd = fbcdcErrorMetaData(self->fbdc, self->cmdMetaData);
758 for (i = 0, count = fbcemdErrorCount(emd); i < count; i++) {
760 const char *errorKind;
763 code = fbcemdErrorCodeAtIndex(emd, i);
764 errorKind = fbcemdErrorKindAtIndex(emd, i);
765 emsg = fbcemdErrorMessageAtIndex(emd, i);
767 NSLog(@" code=%i", code);
768 NSLog(@" kind=%s", errorKind);
769 NSLog(@" msdg=%s", emsg);
771 free((void *)errorKind);
776 if ((msg = fbcemdAllErrorMessages(emd)))
777 error = [NSString stringWithCString:msg];
780 free(msg); msg = NULL;
781 fbcemdRelease(emd); emd = NULL;
782 fbcmdRelease(self->cmdMetaData); self->cmdMetaData = NULL;
784 if (self->sqlLogFile) {
786 if ((fh = fopen([self->sqlLogFile cString], "a"))) {
787 fprintf(fh, "# failed: %s\n",
788 [[error stringByApplyingCEscaping] cString]);
794 NSLog(@"%@: could not execute SQL: %@\n reason: %@",
800 /* init common results */
802 NSAssert(self->cmdMetaData, @"missing command-meta-data");
803 self->numberOfColumns = fbcmdColumnCount(self->cmdMetaData);
804 self->rowsAffected = fbcmdRowCount(self->cmdMetaData);
805 self->txVersion = fbcmdTransactionVersion(self->cmdMetaData);
806 self->isFetchInProgress = NO;
808 if ((self->fetchHandle = fbcmdFetchHandle(self->cmdMetaData)))
809 self->isFetchInProgress = YES;
811 if (self->numberOfColumns > 0) {
814 self->datatypeCodes = calloc(self->numberOfColumns, sizeof(int));
815 for (i = 0; i < self->numberOfColumns; i++) {
816 const FBCDatatypeMetaData *dmd;
818 if ((dmd = fbcmdDatatypeMetaDataAtIndex(self->cmdMetaData, i)))
819 self->datatypeCodes[i] = fbcdmdDatatypeCode(dmd);
821 self->datatypeCodes[i] = FB_VCharacter;
825 if (!self->isFetchInProgress) {
826 fbcmdRelease(self->cmdMetaData);
827 self->cmdMetaData = NULL;
831 /* some constraints */
832 if ([_expression hasPrefix:@"INSERT"]) {
833 NSLog(@"an insert shouldn't start a fetch ! ..");
835 else if ([_expression hasPrefix:@"DELETE"]) {
836 NSLog(@"a delete shouldn't start a fetch ! ..");
838 else if ([_expression hasPrefix:@"UPDATE"]) {
839 NSLog(@"an update shouldn't start a fetch ! ..");
844 /* setup row handler */
847 if (delegateRespondsTo.didEvaluateExpression)
848 [delegate adaptorChannel:self didEvaluateExpression:_expression];
854 if (self->sqlLogFile) {
856 if ((fh = fopen([self->sqlLogFile cString], "a"))) {
858 fprintf(fh, "# %s %3.3g\n", result ? "yes" : "no",
859 -[startDate timeIntervalSinceNow]);
861 fprintf(fh, "# %s\n", result ? "yes" : "no");
873 - (NSString *)description {
874 return [NSString stringWithFormat:@"<%@[0x%08X]: open=%s fetching=%s>",
875 NSStringFromClass([self class]),
877 [self isOpen] ? "yes" : "no",
878 [self isFetchInProgress] ? "yes" : "no"];
881 @end /* FrontBaseChannel */
883 @implementation FrontBaseChannel(PrimaryKeyGeneration)
885 - (NSDictionary *)primaryKeyForNewRowWithEntity:(EOEntity *)_entity {
887 FrontBase2Adaptor *adaptor;
888 NSString *newKeyExpr;
891 pkeys = [_entity primaryKeyAttributeNames];
892 adaptor = (id)[[self adaptorContext] adaptor];
893 newKeyExpr = [adaptor newKeyExpression];
895 if (newKeyExpr == nil) {
896 NSLog(@"ERROR: missing newkey expression, can't gen pkey for %@",
901 if ([pkeys count] != 1) {
902 NSLog(@"no pkeys configured for entity %@", [_entity name]);
909 if ([self evaluateExpression:newKeyExpr]) {
914 attrs = [self describeResults];
915 row = [self fetchAttributes:attrs withZone:NULL];
918 if ((key = [[row objectEnumerator] nextObject])) {
919 pkey = [NSDictionary dictionaryWithObject:
920 [NSNumber numberWithInt:[key intValue]]
921 forKey:[pkeys objectAtIndex:0]];
925 NSLog(@"could not evaluate newkey expression: %@", newKeyExpr);
929 fprintf(stderr, "newkey failed: %s\n",
930 [[localException description] cString]);
939 @end /* FrontBaseChannel(PrimaryKeyGeneration) */
941 @implementation FrontBaseChannel(BlobHandling)
943 - (NSDictionary *)_extractBlobsFromRow:(NSMutableDictionary *)_mrow
944 entity:(EOEntity *)_entity
947 Search for BLOB attributes.
948 If a BLOB attribute is found, remove it from mrow and add it's data to
951 FrontBase2Adaptor *adaptor;
952 NSMutableDictionary *blobRow;
956 adaptor = (FrontBase2Adaptor *)[[self adaptorContext] adaptor];
958 NSAssert(adaptor, @"missing adaptor ..");
961 /* must use allKeys since we are going to modify mrow */
962 keys = [[_mrow allKeys] objectEnumerator];
965 while ((key = [keys nextObject])) {
966 EOAttribute *attribute;
969 attribute = [_entity attributeNamed:key];
970 fbType = [adaptor typeCodeForExternalName:[attribute externalType]];
972 if ((fbType == FB_BLOB) || (fbType == FB_CLOB)) {
976 value = [_mrow objectForKey:key];
977 data = [value dataValueForFrontBaseType:fbType
978 attribute:attribute];
979 if (data == nil) // EONull
983 blobRow = [NSMutableDictionary dictionaryWithCapacity:4];
985 [blobRow setObject:data forKey:key];
986 [_mrow removeObjectForKey:key];
988 else if (fbType == FB_VCharacter) {
989 /* check for large VARCHARs and handle them like BLOBs.. */
992 value = [_mrow objectForKey:key];
994 if ([value isKindOfClass:[NSString class]]) {
995 if ([value length] > 2000) {
998 data = [value dataValueForFrontBaseType:fbType
999 attribute:attribute];
1004 blobRow = [NSMutableDictionary dictionaryWithCapacity:4];
1006 [blobRow setObject:data forKey:key];
1007 [_mrow removeObjectForKey:key];
1010 else if ([value isKindOfClass:[NSData class]]) {
1011 if ([value length] > 60) {
1014 data = [value dataValueForFrontBaseType:fbType
1015 attribute:attribute];
1020 blobRow = [NSMutableDictionary dictionaryWithCapacity:4];
1022 [blobRow setObject:data forKey:key];
1023 [_mrow removeObjectForKey:key];
1029 NSLog(@"%@ is *not* a BLOB attribute (type=%i) ..", attribute, fbType);
1036 - (BOOL)_writeBlobs:(NSDictionary *)_blobs ofRow:(NSMutableDictionary *)_mrow {
1038 This method writes the blobs and set's the appropriate handle-keys in
1042 NSEnumerator *keyEnum;
1045 keyEnum = [_blobs keyEnumerator];
1046 while ((key = [keyEnum nextObject])) {
1048 FBCBlobHandle *blob;
1050 value = [_blobs objectForKey:key];
1053 NSLog(@"writing blob %@ of size %i ...", key, [value length]);
1056 if ((blob = fbcdcWriteBLOB(self->fbdc,
1057 (char*)[value bytes],
1059 char blobid[FBBlobHandleByteSize + 4];
1061 FBBlobHandle *handle;
1063 fbcbhGetHandle(blob, blobid);
1065 blobKey = [NSString stringWithCString:blobid
1066 length:FBBlobHandleByteSize + 3];
1068 NSLog(@" got id %@ for blob %@ ...", blobKey, key);
1070 if (blobKey == nil) {
1076 fbcbhRelease(blob); blob = NULL;
1078 handle = [[FBBlobHandle alloc] initWithBlobID:blobKey];
1079 [_mrow setObject:handle forKey:key];
1080 RELEASE(handle); handle = nil;
1083 NSLog(@"%@: writing of BLOB %@ failed !", self, key);
1093 - (BOOL)insertRow:(NSDictionary *)row forEntity:(EOEntity *)entity {
1094 EOSQLExpression *sqlexpr = nil;
1095 NSMutableDictionary *mrow = nil;
1096 NSDictionary *blobRow = nil;
1098 mrow = AUTORELEASE([row mutableCopyWithZone:[row zone]]);
1101 [[ChannelIsNotOpenedException new] raise];
1103 if((row == nil) || (entity == nil)) {
1104 [[[InvalidArgumentException alloc]
1105 initWithFormat:@"row and entity arguments for insertRow:forEntity:"
1106 @"must not be the nil object"] raise];
1109 if([self isFetchInProgress])
1110 [[AdaptorIsFetchingException exceptionWithAdaptor:self] raise];
1112 if(![adaptorContext transactionNestingLevel])
1113 [[NoTransactionInProgressException exceptionWithAdaptor:self] raise];
1115 if(delegateRespondsTo.willInsertRow) {
1116 EODelegateResponse response;
1118 response = [delegate adaptorChannel:self
1121 if(response == EODelegateRejects)
1123 else if(response == EODelegateOverrides)
1127 /* extract BLOB attributes */
1129 blobRow = [self _extractBlobsFromRow:mrow entity:entity];
1133 if (![self _writeBlobs:blobRow ofRow:mrow])
1136 /* insert non-BLOB attributes */
1138 sqlexpr = [[[adaptorContext adaptor]
1140 insertExpressionForRow:mrow
1144 if(![self evaluateExpression:[sqlexpr expressionValueForContext:nil]])
1147 if(delegateRespondsTo.didInsertRow)
1148 [delegate adaptorChannel:self didInsertRow:mrow forEntity:entity];
1153 - (BOOL)updateRow:(NSDictionary *)row
1154 describedByQualifier:(EOSQLQualifier *)qualifier
1157 FrontBaseChannel's -updateRow:describedByQualifier: differs from the
1158 EOAdaptorChannel one in that it updates BLOB columns seperatly.
1160 FrontBase2Adaptor *adaptor = (FrontBase2Adaptor *)[adaptorContext adaptor];
1161 EOEntity *entity = [qualifier entity];
1162 EOSQLExpression *sqlexpr = nil;
1163 NSMutableDictionary *mrow = nil;
1164 NSDictionary *blobRow = nil;
1166 self->rowsAffected = 0;
1168 mrow = AUTORELEASE([row mutableCopyWithZone:[row zone]]);
1171 [[ChannelIsNotOpenedException new] raise];
1174 [[[InvalidArgumentException alloc]
1175 initWithFormat:@"row argument for updateRow:describedByQualifier: "
1176 @"must not be the nil object"] raise];
1179 if([self isFetchInProgress])
1180 [[AdaptorIsFetchingException exceptionWithAdaptor:self] raise];
1182 if(![adaptorContext transactionNestingLevel])
1183 [[NoTransactionInProgressException exceptionWithAdaptor:self] raise];
1185 if(delegateRespondsTo.willUpdateRow) {
1186 EODelegateResponse response;
1188 response = [delegate adaptorChannel:self
1190 describedByQualifier:qualifier];
1191 if(response == EODelegateRejects)
1193 else if(response == EODelegateOverrides)
1197 /* extract BLOB attributes */
1199 blobRow = [self _extractBlobsFromRow:mrow entity:entity];
1203 if (![self _writeBlobs:blobRow ofRow:mrow])
1206 /* update non-BLOB attributes */
1208 if ([mrow count] > 0) {
1209 sqlexpr = [[adaptor expressionClass]
1210 updateExpressionForRow:mrow
1214 if(![self evaluateExpression:[sqlexpr expressionValueForContext:nil]])
1217 if (self->rowsAffected != 1) {
1218 NSLog(@"%s: rows affected: %i", __PRETTY_FUNCTION__, self->rowsAffected);
1223 /* inform delegate about sucess */
1225 if(delegateRespondsTo.didUpdateRow) {
1226 [delegate adaptorChannel:self
1228 describedByQualifier:qualifier];
1233 - (BOOL)selectAttributes:(NSArray *)attributes
1234 describedByQualifier:(EOSQLQualifier *)qualifier
1235 fetchOrder:(NSArray *)fetchOrder
1238 static Class EOSortOrderingClass = Nil;
1239 NSMutableArray *mattrs;
1240 NSEnumerator *fetchOrders;
1243 /* automatically add attributes used in fetchOrderings (required by FB) */
1245 if (EOSortOrderingClass == Nil)
1246 EOSortOrderingClass = [EOSortOrdering class];
1249 fetchOrders = [fetchOrder objectEnumerator];
1250 while ((fo = [fetchOrders nextObject])) {
1254 if ([fo isKindOfClass:EOSortOrderingClass]) {
1257 attrName = [fo key];
1258 attr = [[qualifier entity] attributeNamed:attrName];
1261 /* EOAttributeOrdering */
1262 attr = [fo attribute];
1266 NSLog(@"ERROR(%s): could not resolve attribute of sort ordering %@ !",
1267 __PRETTY_FUNCTION__, fo);
1272 if (![mattrs containsObject:attr])
1273 [mattrs addObject:attr];
1275 else if (![attributes containsObject:attr]) {
1276 mattrs = [[NSMutableArray alloc] initWithArray:attributes];
1277 [mattrs addObject:attr];
1282 attributes = [mattrs copy];
1284 AUTORELEASE(attributes);
1287 /* continue usual select .. */
1289 ASSIGN(self->selectedAttributes, attributes);
1291 return [super selectAttributes:attributes
1292 describedByQualifier:qualifier
1293 fetchOrder:fetchOrder
1297 @end /* FrontBaseChannel(BlobHandling) */
1299 void __link_FBChannel() {
1300 // used to force linking of object file