4 Copyright (C) SKYRIX Software AG and Helge Hess
8 This file is part of the GNUstep Database 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.
27 column-names must have small letterso
29 EOPrimaryKeyAttributeNamesHint - name of primary key attributes
30 EOPrimaryKeyAttributesHint - primary key attributes
31 EOFetchResultTimeZone - NSTimeZone object for dates
34 #define EOAdaptorDataSource_DEBUG 0
36 #include <NGExtensions/NGExtensions.h>
37 #include <GDLAccess/GDLAccess.h>
38 #include <EOControl/EOControl.h>
41 NSString *EOPrimaryKeyAttributeNamesHint = @"EOPrimaryKeyAttributeNamesHint";
42 NSString *EOPrimaryKeyAttributesHint = @"EOPrimaryKeyAttributesHint";
43 NSString *EOFetchResultTimeZone = @"EOFetchResultTimeZoneHint";
45 @interface NSObject(Private)
46 - (NSString *)newKeyExpression;
47 - (NSArray *)_primaryKeyAttributesForTableName:(NSString *)_entityName
48 channel:(EOAdaptorChannel *)_adChannel;
49 - (NSArray *)_primaryKeyAttributeNamesForTableName:(NSString *)_entityName
50 channel:(EOAdaptorChannel *)_adChannel;
53 static EONull *null = nil;
55 @interface EOAdaptorChannel(Internals)
56 - (NSArray *)_sortAttributesForSelectExpression:(NSArray *)_attrs;
57 @end /* EOAdaptorChannel(Internals) */
59 @interface EOQualifier(SqlExpression)
60 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
61 attributes:(NSArray *)_attrs;
62 @end /* EOQualifier(SqlExpression) */
64 @interface EOAdaptorDataSource(Private)
66 - (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan;
67 - (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan;
68 - (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid
69 adaptor:(EOAdaptor *)_adaptor channel:(EOAdaptorChannel *)_adChan;
71 - (NSString *)_orderByExprForAttributes:(NSArray *)_attrs
72 andPatchSelectList:(NSMutableString *)selectList
73 withChannel:(EOAdaptorChannel *)_adChan;
75 - (NSDictionary *)_mapAttrsWithValues:(NSDictionary *)_keyValues
76 tableName:(NSString *)_tableName channel:(EOAdaptorChannel *)_adChan;
78 @end /* EOAdaptorDataSource(Private) */
80 @interface EOAdaptorDataSource(Internals)
81 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel
82 connectionDictionary:(NSDictionary *)_connDict;
83 @end /* EOAdaptorDataSource(Internals) */
85 @interface EODataSource(Notificiations)
86 - (void)postDataSourceChangedNotification;
89 @implementation EOAdaptorDataSource
91 static Class NSCalendarDateClass = nil;
92 static NSNotificationCenter *nc = nil;
94 static NSNotificationCenter *getNC(void ) {
96 nc = [[NSNotificationCenter defaultCenter] retain];
101 NSAssert2([super version] == 1,
102 @"invalid superclass (%@) version %i !",
103 NSStringFromClass([self superclass]), [super version]);
105 null = [[EONull null] retain];
106 NSCalendarDateClass = [NSCalendarDate class];
109 return [super version] + 1; /* v2 */
112 - (id)initWithAdaptorName:(NSString *)_adName
113 connectionDictionary:(NSDictionary *)_dict
114 primaryKeyGenerationDictionary:(NSDictionary *)_pkGen
117 EOAdaptorContext *ctx = nil;
118 EOAdaptorChannel *adc = nil;
120 ad = [EOAdaptor adaptorWithName:_adName];
121 [ad setConnectionDictionary:_dict];
122 [ad setPkeyGeneratorDictionary:_pkGen];
123 ctx = [ad createAdaptorContext];
124 adc = [ctx createAdaptorChannel];
126 return [self initWithAdaptorChannel:adc connectionDictionary:_dict];
129 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel {
130 return [self initWithAdaptorChannel:_channel connectionDictionary:nil];
133 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel
134 connectionDictionary:(NSDictionary *)_connDict
136 if ((self = [super init])) {
137 self->adChannel = [_channel retain];
138 self->connectionDictionary = [_connDict copy];
139 self->commitTransaction = NO;
142 addObserver:self selector:@selector(_adDataSourceChanged:)
143 name:@"EOAdaptorDataSourceChanged" object:nil];
149 [getNC() removeObserver:self];
150 [self->fetchSpecification release];
151 [self->connectionDictionary release];
152 [self->adChannel release];
153 [self->__attributes release];
154 [self->__qualifier release];
160 - (void)postDataSourceItselfChangedNotification {
161 [super postDataSourceChangedNotification];
164 - (void)postDataSourceChangedNotification {
165 [getNC() postNotificationName:@"EOAdaptorDataSourceChanged" object:self];
166 [self postDataSourceItselfChangedNotification];
169 - (void)_adDataSourceChanged:(NSNotification *)_notification {
170 EOAdaptorDataSource *ads;
172 if ((ads = [_notification object]) == self)
177 [self postDataSourceItselfChangedNotification];
180 if (![ads->connectionDictionary isEqual:self->connectionDictionary])
181 /* different database ... */
184 if ((ads->fetchSpecification == nil) || (self->fetchSpecification == nil)) {
185 [self postDataSourceChangedNotification];
189 /* check fspecs for entity ... */
190 if ([[ads->fetchSpecification entityName]
192 [self->fetchSpecification entityName]]) {
193 [self postDataSourceChangedNotification];
201 - (EOAdaptorChannel *)beginTransaction {
202 EOAdaptorContext *ctx = nil;
205 ctx = [self->adChannel adaptorContext];
206 if ([ctx hasOpenTransaction] == NO) {
207 [ctx beginTransaction];
208 self->commitTransaction = YES;
210 return self->adChannel;
213 - (void)commitTransaction {
214 if (self->commitTransaction) {
215 [[self->adChannel adaptorContext] commitTransaction];
216 // [self->adChannel closeChannel];
217 self->commitTransaction = NO;
221 - (void)rollbackTransaction {
222 [[self->adChannel adaptorContext] rollbackTransaction];
223 // [self->adChannel closeChannel];
224 self->commitTransaction = NO;
227 - (void)openChannel {
228 if (![self->adChannel isOpen]) {
229 [self->adChannel openChannel];
233 - (void)closeChannel {
234 if (![self->adChannel isOpen])
237 if ([[self->adChannel adaptorContext] transactionNestingLevel]) {
238 NSLog(@"%s was called while transaction in progress, rollback will called",
239 __PRETTY_FUNCTION__);
240 [self rollbackTransaction];
242 [self->adChannel closeChannel];
245 - (NSArray *)fetchObjects {
246 // TODO: split up this HUGE method!
247 // TODO: all the SQL gen code should be moved to an appropriate object
248 NSString *entityName = nil;
249 NSString *whereExpr = nil;
250 NSString *orderByExpr = nil;
251 NSMutableString *selectList = nil;
252 NSMutableString *expression = nil;
253 NSMutableArray *result = nil;
254 NSArray *attrs = nil;
255 EOAdaptor *adaptor = nil;
256 NSArray *pKeys = nil;
257 EOQualifier *qual = nil;
258 EOAdaptorChannel *adChan = nil;
260 NSTimeZone *tz = nil;
263 if (self->fetchSpecification == nil) {
264 // TODO: make that a lastException and just return nil
265 [NSException raise:NSInvalidArgumentException
266 format:@"fetchSpecification required for table name"];
270 entityName = [self->fetchSpecification entityName];
272 if (entityName == nil || [entityName length] == 0) {
273 [NSException raise:NSInvalidArgumentException
274 format:@"missing entity name"];
276 localComTrans = [[self->adChannel adaptorContext] hasOpenTransaction]
279 adChan = [self beginTransaction];
280 pKeys = [self _primaryKeyAttributeNamesForTableName:entityName
283 if ((pKeyCnt = [pKeys count]) == 0) {
284 NSLog(@"ERROR[%s]: missing primary keys for table %@",
285 __PRETTY_FUNCTION__, entityName);
288 qual = [self->fetchSpecification qualifier];
291 qual = [EOQualifier qualifierWithQualifierFormat:@"1=1"];
293 ASSIGN(self->__qualifier, qual);
295 attrs = [adChan attributesForTableName:entityName];
298 RELEASE(self->__qualifier); self->__qualifier = nil;
300 NSLog(@"ERROR[%s]: could not find table '%@' in database.",
301 __PRETTY_FUNCTION__, entityName);
302 [self rollbackTransaction];
305 if ([attrs count] == 0) {
306 RELEASE(self->__qualifier); self->__qualifier = nil;
308 NSLog(@"ERROR[%s]: missing columns in table '%@'.",
309 __PRETTY_FUNCTION__, entityName);
310 [self rollbackTransaction];
313 tz = [[self->fetchSpecification hints] objectForKey:EOFetchResultTimeZone];
315 ASSIGN(self->__attributes, attrs);
316 adaptor = [[adChan adaptorContext] adaptor];
319 NSSet *tableKeys = nil;
320 NSSet *qualifierKeys = nil;
322 a = [[[qual allQualifierKeys] allObjects] map:@selector(lowercaseString)];
323 qualifierKeys = [[NSSet alloc] initWithArray:a];
324 a = [[attrs map:@selector(columnName)] map:@selector(lowercaseString)];
325 tableKeys = [[NSSet alloc] initWithArray:a];
327 if ([qualifierKeys isSubsetOfSet:tableKeys] == NO) {
328 NSString *format = nil;
330 format = [NSString stringWithFormat:
331 @"EOAdaptorDataSource: using unmapped key in "
332 @"qualifier tableKeys <%@> qualifierKeys <%@> "
334 tableKeys, qualifierKeys, qual];
336 RELEASE(self->__attributes); self->__attributes = nil;
337 RELEASE(self->__qualifier); self->__qualifier = nil;
338 RELEASE(tableKeys); tableKeys = nil;
339 [self rollbackTransaction];
340 [[[InvalidQualifierException alloc] initWithFormat:format] raise];
342 RELEASE(tableKeys); tableKeys = nil;
343 RELEASE(qualifierKeys); qualifierKeys = nil;
346 whereExpr = [self _whereExprWithChannel:adChan];
347 selectList = [self _selectListWithChannel:adChan];
348 orderByExpr = [self _orderByExprForAttributes:attrs
349 andPatchSelectList:selectList
352 expression = [[NSMutableString alloc] initWithCapacity:256];
353 [expression appendString:@"SELECT "];
355 if ([self->fetchSpecification usesDistinct])
356 [expression appendString:@"DISTINCT "];
358 [expression appendString:selectList];
359 [expression appendString:@" FROM "];
360 [expression appendString:entityName];
361 if ([whereExpr length] > 0) {
362 [expression appendString:@" WHERE "];
363 [expression appendString:whereExpr];
365 if (orderByExpr != nil && [orderByExpr length] > 0) {
366 [expression appendString:@" ORDER BY "];
367 [expression appendString:orderByExpr];
370 if (![adChan evaluateExpression:expression]) {
371 RELEASE(self->__attributes); self->__attributes = nil;
372 RELEASE(self->__qualifier); self->__qualifier = nil;
373 AUTORELEASE(expression);
374 [adChan cancelFetch];
375 [self rollbackTransaction];
376 [[[EOAdaptorException alloc]
377 initWithFormat:@"evaluateExpression of %@ failed", expression] raise];
379 result = [NSMutableArray arrayWithCapacity:64];
381 NSMutableDictionary *row = nil;
382 unsigned fetchCnt = 0;
383 unsigned fetchLimit = 0;
384 unsigned attrCnt = 0;
388 /* Note: those are reused in the inner loop */
389 attrCnt = [attrs count];
390 values = calloc(attrCnt + 2, sizeof(id));
391 keys = calloc(attrCnt + 2, sizeof(id));
392 fetchLimit = [self->fetchSpecification fetchLimit];
394 while ((row = [adChan fetchAttributes:attrs withZone:NULL]) != nil) {
395 NSEnumerator *enumerator = nil;
398 NSDictionary *r = nil;
402 pKeyVs = calloc(pKeyCnt, sizeof(id));
403 enumerator = [attrs objectEnumerator];
405 while ((attr = [enumerator nextObject]) != nil) {
409 obj = [row objectForKey:[(EOAttribute *)attr name]];
414 if (tz != nil && [obj isKindOfClass:NSCalendarDateClass])
415 [obj setTimeZone:tz];
417 cn = [[attr columnName] lowercaseString];
418 values[rowCnt] = obj;
422 if ([pKeys containsObject:cn]) {
425 idx = [pKeys indexOfObject:cn];
426 NSAssert4(idx <= (pKeyCnt - 1) && pKeyVs[idx] == nil,
427 @"internal inconsistency in EOAdaptorDataSource "
428 @"while fetch idx[%d] > (pKeyCnt - 1)[%d] "
429 @"pKeyVs[idx] (%@[%d]);", idx, (pKeyCnt - 1),
436 if (pKeyCnt != pKeyVCnt)
437 NSAssert(NO, @"internal inconsistency in EOAdaptorDataSource "
443 gid = [EOKeyGlobalID globalIDWithEntityName:entityName
444 keys:pKeyVs keyCount:pKeyVCnt zone:NULL];
446 if (self->connectionDictionary) {
447 gid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid
448 connectionDictionary:
449 self->connectionDictionary];
452 values[rowCnt] = gid;
453 keys[rowCnt] = @"globalID";
457 r = [[NSMutableDictionary alloc]
458 initWithObjects:values forKeys:keys count:rowCnt];
459 [result addObject:r];
460 [r release]; r = nil;
461 if (pKeyVs) free(pKeyVs); pKeyVs = NULL;
462 if (fetchLimit == fetchCnt)
465 if (values) free(values); values = NULL;
466 if (keys) free(keys); keys = NULL;
468 [adChan cancelFetch];
470 [self commitTransaction];
472 [expression release]; expression = nil;
473 [self->__qualifier release]; self->__qualifier = nil;
474 [self->__attributes release]; self->__attributes = nil;
479 return [NSMutableDictionary dictionary];
482 - (void)insertObject:(id)_obj {
484 NSString *tableName = nil;
485 NSMutableString *expression = nil;
486 EOAdaptor *adaptor = nil;
487 NSArray *pKeys = nil;
489 EOAdaptorChannel *adChan = nil;
492 NSString **objectKeyAttrValue = NULL;
494 NSEnumerator *enumerator = nil;
499 if ([[self->adChannel adaptorContext] hasOpenTransaction])
504 adChan = [self beginTransaction];
505 adaptor = [[adChan adaptorContext] adaptor];
507 if ((tableName = [self->fetchSpecification entityName]) == nil) {
508 [self rollbackTransaction];
509 [NSException raise:NSInvalidArgumentException
510 format:@"couldn`t insert obj %@ missing entityName in "
511 @"fetchSpecification", _obj];
514 /* create or apply primary keys */
515 #if EOAdaptorDataSource_DEBUG
516 NSLog(@"insert obj %@", _obj);
519 pKeys = [self _primaryKeyAttributeNamesForTableName:tableName channel:adChan];
521 #if EOAdaptorDataSource_DEBUG
522 NSLog(@"got primary keys %@", pKeys);
525 objectKeyAttrValue = calloc([pKeys count], sizeof(id));
526 enumerator = [pKeys objectEnumerator];
528 while ((pKey = [enumerator nextObject])) {
531 pKeyObj = [_obj valueForKey:pKey];
533 #if EOAdaptorDataSource_DEBUG
534 NSLog(@"pk in obj %@:<%@> ", pKey, pKeyObj);
537 if (![pKeyObj isNotNull]) {
538 /* try to build primary key */
539 NSString *newKeyExpr = nil;
540 NSDictionary *row = nil;
542 #if EOAdaptorDataSource_DEBUG
543 NSLog(@"pKeyObj !isNotNull");
546 if ([pKeys count] != 1) {
547 [self rollbackTransaction];
548 [NSException raise:NSInternalInconsistencyException
549 format:@"more than one primary key, "
550 @"and primary key for %@ is not set", pKey];
552 if (![adaptor respondsToSelector:@selector(newKeyExpression)]) {
553 [self rollbackTransaction];
554 [NSException raise:NSInternalInconsistencyException
555 format:@"got no newkey expression, insert failed"];
557 newKeyExpr = [(id)adaptor newKeyExpression];
558 if (newKeyExpr == nil) {
559 [self rollbackTransaction];
560 [NSException raise:NSInternalInconsistencyException
561 format:@"missing newKeyExpression for adaptor[%@] %@...",
562 NSStringFromClass([adaptor class]), adaptor];
564 if (![adChan evaluateExpression:newKeyExpr]) {
565 [adChan cancelFetch];
566 [self rollbackTransaction];
567 [[[EOAdaptorException alloc]
568 initWithFormat:@"couldn`t evaluate new key expression %@",
571 row = [adChan fetchAttributes:[adChan describeResults]
573 [adChan cancelFetch];
574 if ((key = [[row objectEnumerator] nextObject]) == nil) {
575 [self rollbackTransaction];
576 [[[EOAdaptorException alloc]
577 initWithFormat:@"couldn`t fetch primary key"] raise];;
579 objectKeyAttrValue[oVCnt++] = key;
580 [_obj takeValue:key forKey:pKey];
581 #if EOAdaptorDataSource_DEBUG
582 NSLog(@"_obj %@ after takeValue:%@ forKey:%@", _obj, key, pKey);
586 objectKeyAttrValue[oVCnt++] = pKeyObj;
587 #if EOAdaptorDataSource_DEBUG
588 NSLog(@"objectKeyAttrValue takeValue %@ for idx %d", pKeyObj, oVCnt);
593 /* construct SQL INSERT expression .. */
595 expression = [[NSMutableString alloc] initWithCapacity:256];
596 [expression appendString:@"INSERT INTO "];
597 [expression appendString:tableName];
598 [expression appendString:@"("];
600 NSDictionary *objects = nil;
601 NSEnumerator *enumerator = nil;
602 NSArray *allKeys = nil;
605 objects = [self _mapAttrsWithValues:_obj
608 allKeys = [objects allKeys];
609 enumerator = [allKeys objectEnumerator];
610 while ((obj = [enumerator nextObject])) {
614 [expression appendString:@", "];
615 [expression appendString:[adaptor formatAttribute:obj]];
617 [expression appendString:@") VALUES ("];
618 enumerator = [allKeys objectEnumerator];
620 while ((obj = [enumerator nextObject])) {
626 [expression appendString:@", "];
627 value = [objects objectForKey:obj];
628 if (value == nil) value = null;
629 [expression appendString:[adaptor formatValue:value forAttribute:obj]];
632 [expression appendString:@")"];
634 /* execute insert in SQL server .. */
636 if (![adChan evaluateExpression:expression]) {
637 [adChan cancelFetch];
638 enumerator = [pKeys objectEnumerator];
639 while ((pKey = [enumerator nextObject])) {
640 [_obj takeValue:[EONull null] forKey:pKey];
642 [self rollbackTransaction];
643 AUTORELEASE(expression);
644 [[[EOAdaptorException alloc]
645 initWithFormat:@"evaluateExpression %@ failed", expression] raise];
647 [adChan cancelFetch];
649 [self commitTransaction];
651 /* construct new global id for record */
656 gid = [EOKeyGlobalID globalIDWithEntityName:tableName
657 keys:objectKeyAttrValue keyCount:oVCnt zone:NULL];
658 if (self->connectionDictionary != nil) {
659 EOAdaptorGlobalID *agid = nil;
661 agid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid
662 connectionDictionary:
663 self->connectionDictionary];
667 [_obj takeValue:gid forKey:@"globalID"];
670 RELEASE(expression); expression = NULL;
672 /* mark datasource as changed */
674 [self postDataSourceChangedNotification];
677 - (void)updateObject:(id)_obj {
678 NSString *whereClause = nil;
679 NSMutableString *expression = nil;
680 EOAdaptor *adaptor = nil;
681 EOKeyGlobalID *gid = nil;
682 NSEnumerator *enumerator = nil;
683 NSString *tableName = nil;
684 EOAttribute *attr = nil;
686 NSDictionary *objects = nil;
687 EOAdaptorChannel *adChan = nil;
691 if ((gid = [_obj valueForKey:@"globalID"]) == nil) {
692 [NSException raise:NSInvalidArgumentException
693 format:@"missing globalID, couldn`t update"];
695 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) {
696 NSDictionary *conD = nil;
698 conD = [(EOAdaptorGlobalID *)gid connectionDictionary];
699 if (![conD isEqualToDictionary:self->connectionDictionary]) {
700 [NSException raise:NSInvalidArgumentException
701 format:@"try to update object %@ in "
702 @"wrong AdaptorDataSource %@", _obj, self];
704 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID];
706 if ([[self->adChannel adaptorContext] hasOpenTransaction])
711 adChan = [self beginTransaction];
712 tableName = [gid entityName];
713 adaptor = [[adChan adaptorContext] adaptor];
714 whereClause = [self _whereClauseForGlobaID:gid adaptor:adaptor
716 if (whereClause == nil) {
717 [self rollbackTransaction];
720 expression = [[NSMutableString alloc] initWithCapacity:256];
721 [expression appendString:@"UPDATE "];
722 [expression appendString:[gid entityName]];
723 [expression appendString:@" SET "];
725 objects = [self _mapAttrsWithValues:_obj tableName:tableName
727 enumerator = [objects keyEnumerator];
729 while ((attr = [enumerator nextObject])) {
735 [expression appendString:@", "];
736 [expression appendString:[adaptor formatAttribute:attr]];
737 [expression appendString:@"="];
739 value = [objects objectForKey:attr];
740 if (value == nil) value = null;
742 [expression appendString:[adaptor formatValue:value forAttribute:attr]];
744 [expression appendString:@" WHERE "];
745 [expression appendString:whereClause];
746 if (![adChan evaluateExpression:expression]) {
747 [adChan cancelFetch];
748 [self rollbackTransaction];
749 AUTORELEASE(expression);
750 [[[EOAdaptorException alloc]
751 initWithFormat:@"evaluate expression %@ failed", expression] raise];
753 [adChan cancelFetch];
755 [self commitTransaction];
757 RELEASE(expression); expression = nil;
762 NSEnumerator *enumerator;
767 attrs = [self _primaryKeyAttributeNamesForTableName:[gid entityName]
769 enumerator = [attrs objectEnumerator];
771 objs = calloc([attrs count], sizeof(id));
773 while ((attr = [enumerator nextObject])) {
774 objs[objCnt] = [_obj valueForKey:attr];
777 newGID = [EOKeyGlobalID globalIDWithEntityName:[gid entityName]
778 keys:objs keyCount:objCnt zone:NULL];
779 if (self->connectionDictionary != nil) {
780 newGID = [[EOAdaptorGlobalID alloc] initWithGlobalID:newGID
781 connectionDictionary:
782 self->connectionDictionary];
783 [newGID autorelease];
785 [(NSMutableDictionary *)_obj setObject:newGID forKey:@"globalID"];
787 [self postDataSourceChangedNotification];
790 - (void)deleteObject:(id)_obj {
791 NSString *whereClause = nil;
792 NSMutableString *expression = nil;
793 EOKeyGlobalID *gid = nil;
794 EOAdaptorChannel *adChan = nil;
798 if ((gid = [_obj valueForKey:@"globalID"]) == nil) {
799 [NSException raise:NSInvalidArgumentException
800 format:@"missing globalID, could not delete"];
802 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) {
803 NSDictionary *conD = nil;
805 conD = [(EOAdaptorGlobalID *)gid connectionDictionary];
806 if (![conD isEqualToDictionary:self->connectionDictionary]) {
807 [NSException raise:NSInvalidArgumentException
808 format:@"try to delete object %@ in wrong "
809 @"AdaptorDataSource %@", _obj, self];
811 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID];
814 if ([[self->adChannel adaptorContext] hasOpenTransaction])
819 adChan = [self beginTransaction];
820 whereClause = [self _whereClauseForGlobaID:gid
821 adaptor:[[adChan adaptorContext] adaptor] channel:adChan];
822 if (whereClause == nil) {
823 [self rollbackTransaction];
826 expression = [[NSMutableString alloc] initWithCapacity:256];
827 [expression appendString:@"DELETE FROM "];
828 [expression appendString:[gid entityName]];
829 [expression appendString:@" WHERE "];
830 [expression appendString:whereClause];
831 if (![adChan evaluateExpression:expression]) {
832 [adChan cancelFetch];
833 [self rollbackTransaction];
834 AUTORELEASE(expression);
835 [[[EOAdaptorException alloc]
836 initWithFormat:@"couldn`t evaluate expression %@ failed",
839 [adChan cancelFetch];
841 [self commitTransaction];
842 RELEASE(expression); expression = nil;
843 [self postDataSourceChangedNotification];
846 - (void)setFetchSpecification:(EOFetchSpecification *)_fs {
847 if (![self->fetchSpecification isEqual:_fs]) {
849 NSLog(@"%s: 0x%08X: fetch-spec mismatch:\n%@\n%@",
850 __PRETTY_FUNCTION__, self,
851 self->fetchSpecification, _fs);
854 ASSIGNCOPY(self->fetchSpecification, _fs);
856 [self postDataSourceItselfChangedNotification];
860 NSLog(@"%s: 0x%08X: no fetch-spec mismatch:\n%@\n%@\n",
861 __PRETTY_FUNCTION__, self,
862 self->fetchSpecification, _fs);
867 - (EOFetchSpecification *)fetchSpecification {
869 Note: the copy is intended, since the fetchspec is mutable, the consumer
870 could otherwise modify it "behind the scenes"
872 return [[self->fetchSpecification copy] autorelease];
877 - (NSString *)description {
880 ms = [NSMutableString stringWithCapacity:128];
881 [ms appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
883 if (self->fetchSpecification != nil)
884 [ms appendFormat:@" fspec=%@", self->fetchSpecification];
885 if (self->adChannel != nil)
886 [ms appendFormat:@" channel=%@", self->adChannel];
888 [ms appendString:@">"];
892 @end /* EOAdaptorDataSource */
894 @implementation EOAdaptorDataSource(Private)
896 - (NSArray *)_primaryKeyAttributeNamesForTableName:(NSString *)_entityName
897 channel:(EOAdaptorChannel *)_adChannel
902 hints = [self->fetchSpecification hints];
903 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint];
907 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint];
910 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) {
911 attrs = [_adChannel attributesForTableName:_entityName];
915 attrs = [[attrs map:@selector(columnName)] map:@selector(lowercaseString)];
916 attrs = [attrs sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
921 - (NSArray *)_primaryKeyAttributesForTableName:(NSString *)_entityName
922 channel:(EOAdaptorChannel *)_adChannel
927 hints = [self->fetchSpecification hints];
929 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint];
933 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint];
936 NSEnumerator *enumerator;
941 allAttrs = [_adChannel attributesForTableName:_entityName];
942 objs = malloc(sizeof(id) * [allAttrs count]);
943 enumerator = [allAttrs objectEnumerator];
947 while ((obj = [enumerator nextObject])) {
948 if ([attrs containsObject:[[obj columnName] lowercaseString]]) {
949 objs[objCnt++] = obj;
952 attrs = [NSArray arrayWithObjects:objs count:objCnt];
953 free(objs); objs = NULL;
956 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) {
957 attrs = [_adChannel attributesForTableName:_entityName];
962 - (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan {
963 EOQualifier *qual = nil;
964 NSArray *attrs = nil;
965 NSString *entityName = nil;
968 entityName = [self->fetchSpecification entityName];
970 if ((attrs = self->__attributes) == nil)
971 attrs = [_adChan attributesForTableName:entityName];
973 if ((qual = self->__qualifier) == nil)
974 qual = [self->fetchSpecification qualifier];
979 adaptor = [[_adChan adaptorContext] adaptor];
981 return [qual sqlExpressionWithAdaptor:adaptor attributes:attrs];
984 - (NSException *)_couldNotFindSortAttributeInAttributes:(NSArray *)_attrs
985 forSortOrdering:(EOSortOrdering *)_so
987 return [[InvalidAttributeException alloc]
988 initWithFormat:@"could not find EOAttribute for SortOrdering"
989 @" %@ Attributes %@", _so, _attrs];
992 - (EOAttribute *)findAttributeForKey:(NSString *)key
993 inAttributes:(NSArray *)_attrs
998 key = [key lowercaseString];
999 en = [_attrs objectEnumerator];
1000 while ((obj = [en nextObject]) != nil) {
1001 if ([[[obj columnName] lowercaseString] isEqualToString:key])
1007 - (NSString *)_orderByExprForAttributes:(NSArray *)_attrs
1008 andPatchSelectList:(NSMutableString *)selectList
1009 withChannel:(EOAdaptorChannel *)_adChan
1011 NSMutableString *orderByExpr;
1012 NSEnumerator *enumerator = nil;
1013 EOSortOrdering *sortOrdering = nil;
1017 adaptor = [[_adChan adaptorContext] adaptor];
1020 enumerator = [[self->fetchSpecification sortOrderings] objectEnumerator];
1021 while ((sortOrdering = [enumerator nextObject]) != nil) {
1022 SEL selector = NULL;
1023 NSString *key = nil;
1024 EOAttribute *keyAttr = nil;
1025 int order = 0; /* 0 - not used; 1 - asc; 2 - desc */
1026 BOOL inSensitive = NO;
1027 NSString *orderTmp = nil;
1029 if (orderByExpr == nil)
1030 orderByExpr = [NSMutableString stringWithCapacity:64];
1032 [orderByExpr appendString:@", "];
1034 if ((selector = [sortOrdering selector])) {
1035 if (SEL_EQ(selector, EOCompareAscending))
1037 else if (SEL_EQ(selector, EOCompareDescending))
1039 else if (SEL_EQ(selector, EOCompareCaseInsensitiveAscending)) {
1043 else if (SEL_EQ(selector, EOCompareCaseInsensitiveDescending)) {
1048 key = [sortOrdering key];
1050 if (key == nil || [key length] == 0) {
1051 NSLog(@"WARNING[%s]: no key in sortordering %@",
1052 __PRETTY_FUNCTION__, key);
1058 key = [key lowercaseString];
1059 obj = [self findAttributeForKey:key inAttributes:_attrs];
1061 [self->__attributes release]; self->__attributes = nil;
1062 [self->__qualifier release]; self->__qualifier = nil;
1063 #if 0 // TODO: memleak in error case
1064 [expression release]; expression = nil;
1066 [self rollbackTransaction];
1068 [[self _couldNotFindSortAttributeInAttributes:_attrs
1069 forSortOrdering:sortOrdering] raise];
1075 key = [adaptor formatAttribute:keyAttr];
1076 orderTmp = [NSString stringWithFormat:@"order%d", orderCnt];
1078 [orderByExpr appendString:orderTmp];
1080 [orderByExpr appendString:@" ASC"];
1081 else if (order == 2)
1082 [orderByExpr appendString:@" DESC"];
1084 /* manipulate select expr */
1086 if ([[keyAttr valueClassName] isEqualToString:@"NSString"]) {
1087 key = [NSString stringWithFormat:@"LOWER(%@)", key];
1090 NSLog(@"WARNING[%s]: inSensitive expression for no text attribute",
1091 __PRETTY_FUNCTION__);
1094 NSString *str = nil;
1096 str = [key stringByAppendingString:@" AS "];
1097 str = [str stringByAppendingString:orderTmp];
1098 str = [str stringByAppendingString:@", "];
1100 [selectList insertString:str atIndex:0];
1106 - (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan {
1107 NSArray *attrs = nil;
1108 NSEnumerator *enumerator = nil;
1109 EOAttribute *attribute = nil;
1111 NSMutableString *select = nil;
1112 EOAdaptor *adaptor = nil;
1113 NSString *entityName = nil;
1115 adaptor = [[_adChan adaptorContext] adaptor];
1116 entityName = [self->fetchSpecification entityName];
1118 if ((attrs = self->__attributes) == nil)
1119 attrs = [_adChan attributesForTableName:entityName];
1121 attrs = [_adChan _sortAttributesForSelectExpression:attrs];
1122 select = [NSMutableString stringWithCapacity:128];
1123 enumerator = [attrs objectEnumerator];
1124 while ((attribute = [enumerator nextObject])) {
1128 [select appendString:@", "];
1130 [select appendString:[adaptor formatAttribute:attribute]];
1135 - (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid
1136 adaptor:(EOAdaptor *)_adaptor
1137 channel:(EOAdaptorChannel *)_adChan
1139 NSEnumerator *enumerator;
1140 NSMutableString *result;
1147 pKeys = [self _primaryKeyAttributeNamesForTableName:[_gid entityName]
1149 pkAttrs = [self _primaryKeyAttributesForTableName:[_gid entityName]
1153 if ([pKeys count] != [_gid keyCount]) {
1154 NSLog(@"ERROR[%s]: internal inconsitency pkeys %@ gid %@",
1155 __PRETTY_FUNCTION__, pKeys, _gid);
1158 enumerator = [pKeys objectEnumerator];
1162 while ((pKey = [enumerator nextObject])) {
1167 result = [NSMutableString stringWithCapacity:128];
1169 [result appendString:@" AND "];
1172 NSEnumerator *enumerator;
1174 enumerator = [pkAttrs objectEnumerator];
1175 while ((attr = [enumerator nextObject])) {
1176 if ([[[attr columnName] lowercaseString] isEqual:pKey])
1179 NSAssert2(attr != nil, @"missing attribute for pkName %@ attrs %@",
1182 [result appendString:[_adaptor formatAttribute:attr]];
1185 value = [(EOKeyGlobalID *)_gid keyValues][pkCnt++];
1186 if (value == nil) value = null;
1188 [result appendString:[value isNotNull] ? @"=" : @" IS "];
1189 [result appendString:[_adaptor formatValue:value forAttribute:attr]];
1194 - (NSDictionary *)_mapAttrsWithValues:(NSDictionary *)_keyValues
1195 tableName:(NSString *)_tableName
1196 channel:(EOAdaptorChannel *)_adChan
1202 NSDictionary *result;
1205 attrs = [_adChan attributesForTableName:_tableName];
1206 mapCnt = [attrs count];
1207 keys = calloc(mapCnt + 1, sizeof(id));
1208 values = calloc(mapCnt + 1, sizeof(id));
1209 en = [attrs objectEnumerator];
1212 while ((attr = [en nextObject])) {
1215 v = (v = [_keyValues valueForKey:[[attr columnName] lowercaseString]])
1218 keys[mapCnt] = attr;
1222 result = [[NSDictionary alloc]
1223 initWithObjects:values forKeys:keys count:mapCnt];
1224 free(keys); keys = NULL;
1225 free(values); values = NULL;
1226 return [result autorelease];
1229 @end /* EOAdaptorDataSource(Private) */
1231 @implementation EOAndQualifier(SqlExpression)
1233 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1234 attributes:(NSArray *)_attributes
1236 NSMutableString *str = nil;
1237 NSEnumerator *enumerator = nil;
1238 EOQualifier *qual = nil;
1240 NSString *result = nil;
1242 str = [[NSMutableString alloc] initWithCapacity:128];
1244 enumerator = [self->qualifiers objectEnumerator];
1245 while ((qual = [enumerator nextObject])) {
1248 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes];
1250 [str appendFormat:@"(%@)", s];
1254 [str appendFormat:@" AND (%@)", s];
1256 result = [str copy];
1257 [str release]; str = nil;
1258 return [result autorelease];
1260 @end /* EOAndQualifier(SqlExpression) */
1262 @implementation EOOrQualifier(SqlExpression)
1264 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1265 attributes:(NSArray *)_attributes
1267 NSMutableString *str = nil;
1268 NSEnumerator *enumerator = nil;
1269 EOQualifier *qual = nil;
1271 NSString *result = nil;
1273 str = [[NSMutableString alloc] initWithCapacity:128];
1275 enumerator = [self->qualifiers objectEnumerator];
1276 while ((qual = [enumerator nextObject])) {
1279 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes];
1281 [str appendFormat:@"(%@)", s];
1285 [str appendFormat:@" OR (%@)", s];
1287 result = [str copy];
1288 [str release]; str = nil;
1289 return [result autorelease];
1292 @end /* EOOrQualifier(SqlExpression) */
1294 @implementation EOKeyValueQualifier(SqlExpression)
1296 + (NSString *)sqlStringForOperatorSelector:(SEL)_sel {
1297 static NSMapTable *selectorToOperator = NULL;
1300 if ((s = NSStringFromSelector(_sel)) == nil)
1303 if (selectorToOperator == NULL) {
1304 selectorToOperator = NSCreateMapTable(NSObjectMapKeyCallBacks,
1305 NSObjectMapValueCallBacks,
1307 NSMapInsert(selectorToOperator,
1308 NSStringFromSelector(EOQualifierOperatorEqual),
1310 NSMapInsert(selectorToOperator,
1311 NSStringFromSelector(EOQualifierOperatorNotEqual),
1313 NSMapInsert(selectorToOperator,
1314 NSStringFromSelector(EOQualifierOperatorLessThan),
1316 NSMapInsert(selectorToOperator,
1317 NSStringFromSelector(EOQualifierOperatorGreaterThan),
1319 NSMapInsert(selectorToOperator,
1320 NSStringFromSelector(EOQualifierOperatorLessThanOrEqualTo),
1322 NSMapInsert(selectorToOperator,
1323 NSStringFromSelector(EOQualifierOperatorGreaterThanOrEqualTo),
1327 if ((ss = NSMapGet(selectorToOperator, s)))
1333 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1334 attributes:(NSArray *)_attributes
1336 EOAttribute *attr = nil;
1337 NSEnumerator *en = nil;
1339 NSString *sql = nil;
1340 NSString *sqlKey, *sqlValue;
1342 k = [self->key lowercaseString];
1343 en = [_attributes objectEnumerator];
1345 while ((attr = [en nextObject])) {
1346 if ([[[attr columnName] lowercaseString] isEqualToString:k]) {
1351 en = [_attributes objectEnumerator];
1352 while ((attr = [en nextObject])) {
1353 if ([[attr name] isEqualToString:self->key])
1358 NSLog(@"WARNING[%s]: missing attribute [%@] for qualifier %@",
1359 __PRETTY_FUNCTION__,
1364 sqlKey = [_adaptor formatAttribute:attr];
1366 sqlValue = [_adaptor formatValue:self->value ? self->value : null
1371 if (SEL_EQ(EOQualifierOperatorEqual, self->operator)) {
1372 if ([self->value isNotNull])
1373 sql = [NSString stringWithFormat:@"%@ = %@", sqlKey, sqlValue];
1375 sql = [NSString stringWithFormat:@"%@ IS NULL", sqlKey];
1377 else if (SEL_EQ(EOQualifierOperatorNotEqual, self->operator)) {
1378 if ([self->value isNotNull])
1379 sql = [NSString stringWithFormat:@"NOT (%@ = %@)", sqlKey, sqlValue];
1381 sql = [NSString stringWithFormat:@"%@ IS NOT NULL", sqlKey];
1383 else if (SEL_EQ(EOQualifierOperatorLessThan, self->operator)) {
1384 sql = [NSString stringWithFormat:@"%@ < %@", sqlKey, sqlValue];
1386 else if (SEL_EQ(EOQualifierOperatorLessThanOrEqualTo, self->operator)) {
1387 sql = [NSString stringWithFormat:@"%@ <= %@", sqlKey, sqlValue];
1389 else if (SEL_EQ(EOQualifierOperatorGreaterThan, self->operator)) {
1390 sql = [NSString stringWithFormat:@"%@ > %@", sqlKey, sqlValue];
1392 else if (SEL_EQ(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) {
1393 sql = [NSString stringWithFormat:@"%@ >= %@", sqlKey, sqlValue];
1395 else if (SEL_EQ(EOQualifierOperatorLike, self->operator)) {
1396 sqlValue = [[self->value stringValue]
1397 stringByReplacingString:@"*" withString:@"%"];
1398 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr];
1400 sql = [NSString stringWithFormat:@"%@ LIKE %@", sqlKey, sqlValue];
1402 else if (SEL_EQ(EOQualifierOperatorCaseInsensitiveLike, self->operator)) {
1403 sqlValue = [[self->value stringValue]
1404 stringByReplacingString:@"*" withString:@"%"];
1405 sqlValue = [sqlValue lowercaseString];
1406 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr];
1408 sql = [NSString stringWithFormat:@"LOWER(%@) LIKE %@", sqlKey, sqlValue];
1411 else if (SEL_EQ(EOQualifierOperatorLessThanOrEqualTo, self->operator)) {
1413 else if (SEL_EQ(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) {
1417 NSLog(@"ERROR(%s): unsupported SQL operator: %@", __PRETTY_FUNCTION__,
1418 [EOQualifier stringForOperatorSelector:self->operator]);
1419 [self notImplemented:_cmd];
1426 @end /* EOKeyValueQualifier(SqlExpression) */
1428 @implementation EONotQualifier(SqlExpression)
1430 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1431 attributes:(NSArray *)_attributes
1435 s = [self->qualifier sqlExpressionWithAdaptor:_adaptor
1436 attributes:_attributes];
1437 return [NSString stringWithFormat:@"NOT(%@)", s];
1440 @end /* EONotQualifier(SqlExpression) */
1442 @implementation EOKeyComparisonQualifier(SqlExpression)
1444 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1445 attributes:(NSArray *)_attributes
1447 NSLog(@"ERROR(%s): subclass needs to override this method!",
1448 __PRETTY_FUNCTION__);
1452 @end /* EOKeyComparisonQualifier(SqlExpression) */