4 Copyright (C) SKYRIX Software AG and Helge Hess
6 Author: Helge Hess (helge.hess@opengroupware.org)
9 This file is part of the GNUstep Database Library.
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.
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.
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.
26 // $Id: EOAdaptorDataSource.m 1 2004-08-20 10:38:46Z znek $
29 column-names must have small letterso
31 EOPrimaryKeyAttributeNamesHint - name of primary key attributes
32 EOPrimaryKeyAttributesHint - primary key attributes
33 EOFetchResultTimeZone - NSTimeZone object for dates
36 #define EOAdaptorDataSource_DEBUG 0
38 #include <NGExtensions/NGExtensions.h>
39 #include <GDLAccess/GDLAccess.h>
40 #include <EOControl/EOControl.h>
43 NSString *EOPrimaryKeyAttributeNamesHint = @"EOPrimaryKeyAttributeNamesHint";
44 NSString *EOPrimaryKeyAttributesHint = @"EOPrimaryKeyAttributesHint";
45 NSString *EOFetchResultTimeZone = @"EOFetchResultTimeZoneHint";
47 @interface NSObject(Private)
48 - (NSString *)newKeyExpression;
49 - (NSArray *)_primaryKeyAttributesForTableName:(NSString *)_entityName
50 channel:(EOAdaptorChannel *)_adChannel;
51 - (NSArray *)_primaryKeyAttributeNamesForTableName:(NSString *)_entityName
52 channel:(EOAdaptorChannel *)_adChannel;
55 static EONull *null = nil;
57 @interface EOAdaptorChannel(Internals)
58 - (NSArray *)_sortAttributesForSelectExpression:(NSArray *)_attrs;
59 @end /* EOAdaptorChannel(Internals) */
61 @interface EOQualifier(SqlExpression)
62 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
63 attributes:(NSArray *)_attrs;
64 @end /* EOQualifier(SqlExpression) */
66 @interface EOAdaptorDataSource(Private)
67 - (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan;
68 - (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan;
69 - (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid
70 adaptor:(EOAdaptor *)_adaptor channel:(EOAdaptorChannel *)_adChan;
71 - (NSDictionary *)_mapAttrsWithValues:(NSDictionary *)_keyValues
72 tableName:(NSString *)_tableName channel:(EOAdaptorChannel *)_adChan;
73 @end /* EOAdaptorDataSource(Private) */
75 @interface EOAdaptorDataSource(Internals)
76 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel
77 connectionDictionary:(NSDictionary *)_connDict;
78 @end /* EOAdaptorDataSource(Internals) */
80 @interface EODataSource(Notificiations)
81 - (void)postDataSourceChangedNotification;
84 @implementation EOAdaptorDataSource
86 static NSNotificationCenter *nc = nil;
88 static NSNotificationCenter *getNC(void ) {
90 nc = [[NSNotificationCenter defaultCenter] retain];
95 NSAssert2([super version] == 1,
96 @"invalid superclass (%@) version %i !",
97 NSStringFromClass([self superclass]), [super version]);
99 null = [[EONull null] retain];
102 return [super version] + 1; /* v2 */
105 - (id)initWithAdaptorName:(NSString *)_adName
106 connectionDictionary:(NSDictionary *)_dict
107 primaryKeyGenerationDictionary:(NSDictionary *)_pkGen
110 EOAdaptorContext *ctx = nil;
111 EOAdaptorChannel *adc = nil;
113 ad = [EOAdaptor adaptorWithName:_adName];
114 [ad setConnectionDictionary:_dict];
115 [ad setPkeyGeneratorDictionary:_pkGen];
116 ctx = [ad createAdaptorContext];
117 adc = [ctx createAdaptorChannel];
119 return [self initWithAdaptorChannel:adc connectionDictionary:_dict];
122 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel {
123 return [self initWithAdaptorChannel:_channel connectionDictionary:nil];
126 - (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel
127 connectionDictionary:(NSDictionary *)_connDict
129 if ((self = [super init])) {
130 self->adChannel = [_channel retain];
131 self->connectionDictionary = [_connDict copy];
132 self->commitTransaction = NO;
135 addObserver:self selector:@selector(_adDataSourceChanged:)
136 name:@"EOAdaptorDataSourceChanged" object:nil];
142 [getNC() removeObserver:self];
143 [self->fetchSpecification release];
144 [self->connectionDictionary release];
145 [self->adChannel release];
146 [self->__attributes release];
147 [self->__qualifier release];
153 - (void)postDataSourceItselfChangedNotification {
154 [super postDataSourceChangedNotification];
157 - (void)postDataSourceChangedNotification {
158 [getNC() postNotificationName:@"EOAdaptorDataSourceChanged" object:self];
159 [self postDataSourceItselfChangedNotification];
162 - (void)_adDataSourceChanged:(NSNotification *)_notification {
163 EOAdaptorDataSource *ads;
165 if ((ads = [_notification object]) == self)
170 [self postDataSourceItselfChangedNotification];
173 if (![ads->connectionDictionary isEqual:self->connectionDictionary])
174 /* different database ... */
177 if ((ads->fetchSpecification == nil) || (self->fetchSpecification == nil)) {
178 [self postDataSourceChangedNotification];
182 /* check fspecs for entity ... */
183 if ([[ads->fetchSpecification entityName]
185 [self->fetchSpecification entityName]]) {
186 [self postDataSourceChangedNotification];
194 - (EOAdaptorChannel *)beginTransaction {
195 EOAdaptorContext *ctx = nil;
198 ctx = [self->adChannel adaptorContext];
199 if ([ctx hasOpenTransaction] == NO) {
200 [ctx beginTransaction];
201 self->commitTransaction = YES;
203 return self->adChannel;
206 - (void)commitTransaction {
207 if (self->commitTransaction) {
208 [[self->adChannel adaptorContext] commitTransaction];
209 // [self->adChannel closeChannel];
210 self->commitTransaction = NO;
214 - (void)rollbackTransaction {
215 [[self->adChannel adaptorContext] rollbackTransaction];
216 // [self->adChannel closeChannel];
217 self->commitTransaction = NO;
220 - (void)openChannel {
221 if (![self->adChannel isOpen]) {
222 [self->adChannel openChannel];
226 - (void)closeChannel {
227 if (![self->adChannel isOpen])
230 if ([[self->adChannel adaptorContext] transactionNestingLevel]) {
231 NSLog(@"%s was called while transaction in progress, rollback will called",
232 __PRETTY_FUNCTION__);
233 [self rollbackTransaction];
235 [self->adChannel closeChannel];
238 - (NSArray *)fetchObjects {
239 /* TODO: split up this HUGE method! */
240 NSString *entityName = nil;
241 NSString *whereExpr = nil;
242 NSMutableString *orderByExpr = nil;
243 NSMutableString *selectList = nil;
244 NSMutableString *expression = nil;
245 NSMutableArray *result = nil;
246 NSArray *attrs = nil;
247 EOAdaptor *adaptor = nil;
248 NSArray *pKeys = nil;
249 EOQualifier *qual = nil;
250 EOAdaptorChannel *adChan = nil;
252 NSTimeZone *tz = nil;
255 if (self->fetchSpecification == nil) {
256 [NSException raise:NSInvalidArgumentException
257 format:@"fetchSpecification required for table name"];
261 entityName = [self->fetchSpecification entityName];
263 if (entityName == nil || [entityName length] == 0) {
264 [NSException raise:NSInvalidArgumentException
265 format:@"missing entity name"];
267 localComTrans = [[self->adChannel adaptorContext] hasOpenTransaction]
270 adChan = [self beginTransaction];
271 pKeys = [self _primaryKeyAttributeNamesForTableName:entityName
274 if ((pKeyCnt = [pKeys count]) == 0) {
275 NSLog(@"ERROR[%s]: missing primary keys for table %@",
276 __PRETTY_FUNCTION__, entityName);
279 qual = [self->fetchSpecification qualifier];
282 qual = [EOQualifier qualifierWithQualifierFormat:@"1=1"];
284 ASSIGN(self->__qualifier, qual);
286 attrs = [adChan attributesForTableName:entityName];
289 RELEASE(self->__qualifier); self->__qualifier = nil;
291 NSLog(@"ERROR[%s]: could not find table '%@' in database.",
292 __PRETTY_FUNCTION__, entityName);
293 [self rollbackTransaction];
296 if ([attrs count] == 0) {
297 RELEASE(self->__qualifier); self->__qualifier = nil;
299 NSLog(@"ERROR[%s]: missing columns in table '%@'.",
300 __PRETTY_FUNCTION__, entityName);
301 [self rollbackTransaction];
304 tz = [[self->fetchSpecification hints] objectForKey:EOFetchResultTimeZone];
306 ASSIGN(self->__attributes, attrs);
307 adaptor = [[adChan adaptorContext] adaptor];
310 NSSet *tableKeys = nil;
311 NSSet *qualifierKeys = nil;
313 a = [[[qual allQualifierKeys] allObjects] map:@selector(lowercaseString)];
314 qualifierKeys = [[NSSet alloc] initWithArray:a];
315 a = [[attrs map:@selector(columnName)] map:@selector(lowercaseString)];
316 tableKeys = [[NSSet alloc] initWithArray:a];
318 if ([qualifierKeys isSubsetOfSet:tableKeys] == NO) {
319 NSString *format = nil;
321 format = [NSString stringWithFormat:
322 @"EOAdaptorDataSource: using unmapped key in "
323 @"qualifier tableKeys <%@> qualifierKeys <%@> "
325 tableKeys, qualifierKeys, qual];
327 RELEASE(self->__attributes); self->__attributes = nil;
328 RELEASE(self->__qualifier); self->__qualifier = nil;
329 RELEASE(tableKeys); tableKeys = nil;
330 [self rollbackTransaction];
331 [[[InvalidQualifierException alloc] initWithFormat:format] raise];
333 RELEASE(tableKeys); tableKeys = nil;
334 RELEASE(qualifierKeys); qualifierKeys = nil;
337 whereExpr = [self _whereExprWithChannel:adChan];
338 selectList = [self _selectListWithChannel:adChan];
339 { /* order by expr */
340 NSEnumerator *enumerator = nil;
341 EOSortOrdering *sortOrdering = nil;
344 enumerator = [[self->fetchSpecification sortOrderings] objectEnumerator];
346 while ((sortOrdering = [enumerator nextObject])) {
349 EOAttribute *keyAttr = nil;
350 int order = 0; /* 0 - not used; 1 - asc; 2 - desc */
351 BOOL inSensitive = NO;
352 NSString *orderTmp = nil;
354 if (orderByExpr == nil) {
355 orderByExpr = [NSMutableString stringWithCapacity:64];
358 [orderByExpr appendString:@", "];
360 if ((selector = [sortOrdering selector])) {
361 if (SEL_EQ(selector, EOCompareAscending)) {
364 else if (SEL_EQ(selector, EOCompareDescending)) {
367 else if (SEL_EQ(selector, EOCompareCaseInsensitiveAscending)) {
371 else if (SEL_EQ(selector, EOCompareCaseInsensitiveDescending)) {
376 key = [sortOrdering key];
378 if (key == nil || [key length] == 0) {
379 NSLog(@"WARNING[%s]: wrong key in sortordering %@",
380 __PRETTY_FUNCTION__, key);
384 NSEnumerator *en = nil;
387 key = [key lowercaseString];
388 en = [attrs objectEnumerator];
389 while ((obj = [en nextObject])) {
390 if ([[[(EOAttribute *)obj columnName] lowercaseString]
391 isEqualToString:key])
395 RELEASE(self->__attributes); self->__attributes = nil;
396 RELEASE(self->__qualifier); self->__qualifier = nil;
397 RELEASE(expression); expression = nil;
398 [self rollbackTransaction];
399 [[[InvalidAttributeException alloc]
400 initWithFormat:@"couldn`t find EOAttribute for SortOrdering"
401 @" %@ Attributes %@",
402 sortOrdering, attrs] raise];
406 key = [adaptor formatAttribute:keyAttr];
407 orderTmp = [NSString stringWithFormat:@"order_by_expr_%d", orderCnt++];
408 [orderByExpr appendString:orderTmp];
410 [orderByExpr appendString:@" ASC"];
412 else if (order == 2) {
413 [orderByExpr appendString:@" DESC"];
415 { /* manipulate select expr */
417 if ([[keyAttr valueClassName] isEqualToString:@"NSString"]) {
418 key = [NSString stringWithFormat:@"LOWER(%@)", key];
421 NSLog(@"WARNING[%s]: inSensitive expression for no text attribute",
422 __PRETTY_FUNCTION__);
427 str = [key stringByAppendingString:@" "];
428 str = [str stringByAppendingString:orderTmp];
429 str = [str stringByAppendingString:@", "];
431 [selectList insertString:str atIndex:0];
436 expression = [[NSMutableString alloc] initWithCapacity:256];
437 [expression appendString:@"SELECT "];
439 if ([self->fetchSpecification usesDistinct])
440 [expression appendString:@"DISTINCT "];
442 [expression appendString:selectList];
443 [expression appendString:@" FROM "];
444 [expression appendString:entityName];
445 if ([whereExpr length] > 0) {
446 [expression appendString:@" WHERE "];
447 [expression appendString:whereExpr];
449 if (orderByExpr != nil && [orderByExpr length] > 0) {
450 [expression appendString:@" ORDER BY "];
451 [expression appendString:orderByExpr];
454 if (![adChan evaluateExpression:expression]) {
455 RELEASE(self->__attributes); self->__attributes = nil;
456 RELEASE(self->__qualifier); self->__qualifier = nil;
457 AUTORELEASE(expression);
458 [adChan cancelFetch];
459 [self rollbackTransaction];
460 [[[EOAdaptorException alloc]
461 initWithFormat:@"evaluateExpression of %@ failed", expression] raise];
463 result = [NSMutableArray arrayWithCapacity:64];
465 NSMutableDictionary *row = nil;
466 unsigned fetchCnt = 0;
467 unsigned fetchLimit = 0;
468 unsigned attrCnt = 0;
472 attrCnt = [attrs count];
473 values = calloc(attrCnt + 2, sizeof(id));
474 keys = calloc(attrCnt + 2, sizeof(id));
475 fetchLimit = [self->fetchSpecification fetchLimit];
477 while ((row = [adChan fetchAttributes:attrs withZone:NULL])) {
478 NSEnumerator *enumerator = nil;
481 NSDictionary *r = nil;
485 pKeyVs = calloc(pKeyCnt, sizeof(id));
486 enumerator = [attrs objectEnumerator];
488 while ((attr = [enumerator nextObject])) {
493 obj = [row objectForKey:[(EOAttribute *)attr name]];
499 static Class NSCalendarDateClass = nil;
501 if (NSCalendarDateClass == nil)
502 NSCalendarDateClass = [NSCalendarDate class];
504 if ([obj isKindOfClass:NSCalendarDateClass]) {
505 [obj setTimeZone:tz];
508 cn = [[attr columnName] lowercaseString];
509 values[rowCnt] = obj;
513 if ([pKeys containsObject:cn]) {
516 idx = [pKeys indexOfObject:cn];
517 NSAssert4(idx <= (pKeyCnt - 1) && pKeyVs[idx] == nil,
518 @"internal inconsistency in EOAdaptorDataSource "
519 @"while fetch idx[%d] > (pKeyCnt - 1)[%d] "
520 @"pKeyVs[idx] (%@[%d]);", idx, (pKeyCnt - 1),
527 if (pKeyCnt != pKeyVCnt)
528 NSAssert(NO, @"internal inconsistency in EOAdaptorDataSource "
534 gid = [EOKeyGlobalID globalIDWithEntityName:entityName
535 keys:pKeyVs keyCount:pKeyVCnt zone:NULL];
537 if (self->connectionDictionary) {
538 gid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid
539 connectionDictionary:
540 self->connectionDictionary];
543 values[rowCnt] = gid;
544 keys[rowCnt] = @"globalID";
548 r = [[NSMutableDictionary alloc]
549 initWithObjects:values forKeys:keys count:rowCnt];
550 [result addObject:r];
552 free(pKeyVs); pKeyVs = NULL;
553 if (fetchLimit == fetchCnt) {
557 free(values); values = NULL;
558 free(keys); keys = NULL;
560 [adChan cancelFetch];
562 [self commitTransaction];
564 RELEASE(expression); expression = nil;
565 RELEASE(self->__qualifier); self->__qualifier = nil;
566 RELEASE(self->__attributes); self->__attributes = nil;
571 return [NSMutableDictionary dictionary];
574 - (void)insertObject:(id)_obj {
576 NSString *tableName = nil;
577 NSMutableString *expression = nil;
578 EOAdaptor *adaptor = nil;
579 NSArray *pKeys = nil;
581 EOAdaptorChannel *adChan = nil;
584 NSString **objectKeyAttrValue = NULL;
586 NSEnumerator *enumerator = nil;
591 if ([[self->adChannel adaptorContext] hasOpenTransaction])
596 adChan = [self beginTransaction];
597 adaptor = [[adChan adaptorContext] adaptor];
599 if ((tableName = [self->fetchSpecification entityName]) == nil) {
600 [self rollbackTransaction];
601 [NSException raise:NSInvalidArgumentException
602 format:@"couldn`t insert obj %@ missing entityName in "
603 @"fetchSpecification", _obj];
606 /* create or apply primary keys */
607 #if EOAdaptorDataSource_DEBUG
608 NSLog(@"insert obj %@", _obj);
611 pKeys = [self _primaryKeyAttributeNamesForTableName:tableName channel:adChan];
613 #if EOAdaptorDataSource_DEBUG
614 NSLog(@"got primary keys %@", pKeys);
617 objectKeyAttrValue = calloc([pKeys count], sizeof(id));
618 enumerator = [pKeys objectEnumerator];
620 while ((pKey = [enumerator nextObject])) {
623 pKeyObj = [_obj valueForKey:pKey];
625 #if EOAdaptorDataSource_DEBUG
626 NSLog(@"pk in obj %@:<%@> ", pKey, pKeyObj);
629 if (![pKeyObj isNotNull]) {
630 /* try to build primary key */
631 NSString *newKeyExpr = nil;
632 NSDictionary *row = nil;
634 #if EOAdaptorDataSource_DEBUG
635 NSLog(@"pKeyObj !isNotNull");
638 if ([pKeys count] != 1) {
639 [self rollbackTransaction];
640 [NSException raise:NSInternalInconsistencyException
641 format:@"more than one primary key, "
642 @"and primary key for %@ is not set", pKey];
644 if (![adaptor respondsToSelector:@selector(newKeyExpression)]) {
645 [self rollbackTransaction];
646 [NSException raise:NSInternalInconsistencyException
647 format:@"got no newkey expression, insert failed"];
649 newKeyExpr = [(id)adaptor newKeyExpression];
650 if (newKeyExpr == nil) {
651 [self rollbackTransaction];
652 [NSException raise:NSInternalInconsistencyException
653 format:@"missing newKeyExpression for adaptor[%@] %@...",
654 NSStringFromClass([adaptor class]), adaptor];
656 if (![adChan evaluateExpression:newKeyExpr]) {
657 [adChan cancelFetch];
658 [self rollbackTransaction];
659 [[[EOAdaptorException alloc]
660 initWithFormat:@"couldn`t evaluate new key expression %@",
663 row = [adChan fetchAttributes:[adChan describeResults]
665 [adChan cancelFetch];
666 if ((key = [[row objectEnumerator] nextObject]) == nil) {
667 [self rollbackTransaction];
668 [[[EOAdaptorException alloc]
669 initWithFormat:@"couldn`t fetch primary key"] raise];;
671 objectKeyAttrValue[oVCnt++] = key;
672 [_obj takeValue:key forKey:pKey];
673 #if EOAdaptorDataSource_DEBUG
674 NSLog(@"_obj %@ after takeValue:%@ forKey:%@", _obj, key, pKey);
678 objectKeyAttrValue[oVCnt++] = pKeyObj;
679 #if EOAdaptorDataSource_DEBUG
680 NSLog(@"objectKeyAttrValue takeValue %@ for idx %d", pKeyObj, oVCnt);
685 /* construct SQL INSERT expression .. */
687 expression = [[NSMutableString alloc] initWithCapacity:256];
688 [expression appendString:@"INSERT INTO "];
689 [expression appendString:tableName];
690 [expression appendString:@"("];
692 NSDictionary *objects = nil;
693 NSEnumerator *enumerator = nil;
694 NSArray *allKeys = nil;
697 objects = [self _mapAttrsWithValues:_obj
700 allKeys = [objects allKeys];
701 enumerator = [allKeys objectEnumerator];
702 while ((obj = [enumerator nextObject])) {
706 [expression appendString:@", "];
707 [expression appendString:[adaptor formatAttribute:obj]];
709 [expression appendString:@") VALUES ("];
710 enumerator = [allKeys objectEnumerator];
712 while ((obj = [enumerator nextObject])) {
718 [expression appendString:@", "];
719 value = [objects objectForKey:obj];
720 if (value == nil) value = null;
721 [expression appendString:[adaptor formatValue:value forAttribute:obj]];
724 [expression appendString:@")"];
726 /* execute insert in SQL server .. */
728 if (![adChan evaluateExpression:expression]) {
729 [adChan cancelFetch];
730 enumerator = [pKeys objectEnumerator];
731 while ((pKey = [enumerator nextObject])) {
732 [_obj takeValue:[EONull null] forKey:pKey];
734 [self rollbackTransaction];
735 AUTORELEASE(expression);
736 [[[EOAdaptorException alloc]
737 initWithFormat:@"evaluateExpression %@ failed", expression] raise];
739 [adChan cancelFetch];
741 [self commitTransaction];
743 /* construct new global id for record */
748 gid = [EOKeyGlobalID globalIDWithEntityName:tableName
749 keys:objectKeyAttrValue keyCount:oVCnt zone:NULL];
750 if (self->connectionDictionary != nil) {
751 EOAdaptorGlobalID *agid = nil;
753 agid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid
754 connectionDictionary:
755 self->connectionDictionary];
759 [_obj takeValue:gid forKey:@"globalID"];
762 RELEASE(expression); expression = NULL;
764 /* mark datasource as changed */
766 [self postDataSourceChangedNotification];
769 - (void)updateObject:(id)_obj {
770 NSString *whereClause = nil;
771 NSMutableString *expression = nil;
772 EOAdaptor *adaptor = nil;
773 EOKeyGlobalID *gid = nil;
774 NSEnumerator *enumerator = nil;
775 NSString *tableName = nil;
776 EOAttribute *attr = nil;
778 NSDictionary *objects = nil;
779 EOAdaptorChannel *adChan = nil;
783 if ((gid = [_obj valueForKey:@"globalID"]) == nil) {
784 [NSException raise:NSInvalidArgumentException
785 format:@"missing globalID, couldn`t update"];
787 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) {
788 NSDictionary *conD = nil;
790 conD = [(EOAdaptorGlobalID *)gid connectionDictionary];
791 if (![conD isEqualToDictionary:self->connectionDictionary]) {
792 [NSException raise:NSInvalidArgumentException
793 format:@"try to update object %@ in "
794 @"wrong AdaptorDataSource %@", _obj, self];
796 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID];
798 if ([[self->adChannel adaptorContext] hasOpenTransaction])
803 adChan = [self beginTransaction];
804 tableName = [gid entityName];
805 adaptor = [[adChan adaptorContext] adaptor];
806 whereClause = [self _whereClauseForGlobaID:gid adaptor:adaptor
808 if (whereClause == nil) {
809 [self rollbackTransaction];
812 expression = [[NSMutableString alloc] initWithCapacity:256];
813 [expression appendString:@"UPDATE "];
814 [expression appendString:[gid entityName]];
815 [expression appendString:@" SET "];
817 objects = [self _mapAttrsWithValues:_obj tableName:tableName
819 enumerator = [objects keyEnumerator];
821 while ((attr = [enumerator nextObject])) {
827 [expression appendString:@", "];
828 [expression appendString:[adaptor formatAttribute:attr]];
829 [expression appendString:@"="];
831 value = [objects objectForKey:attr];
832 if (value == nil) value = null;
834 [expression appendString:[adaptor formatValue:value forAttribute:attr]];
836 [expression appendString:@" WHERE "];
837 [expression appendString:whereClause];
838 if (![adChan evaluateExpression:expression]) {
839 [adChan cancelFetch];
840 [self rollbackTransaction];
841 AUTORELEASE(expression);
842 [[[EOAdaptorException alloc]
843 initWithFormat:@"evaluate expression %@ failed", expression] raise];
845 [adChan cancelFetch];
847 [self commitTransaction];
849 RELEASE(expression); expression = nil;
854 NSEnumerator *enumerator;
859 attrs = [self _primaryKeyAttributeNamesForTableName:[gid entityName]
861 enumerator = [attrs objectEnumerator];
863 objs = calloc([attrs count], sizeof(id));
865 while ((attr = [enumerator nextObject])) {
866 objs[objCnt] = [_obj valueForKey:attr];
869 newGID = [EOKeyGlobalID globalIDWithEntityName:[gid entityName]
870 keys:objs keyCount:objCnt zone:NULL];
871 if (self->connectionDictionary != nil) {
872 newGID = [[EOAdaptorGlobalID alloc] initWithGlobalID:newGID
873 connectionDictionary:
874 self->connectionDictionary];
875 [newGID autorelease];
877 [_obj setObject:newGID forKey:@"globalID"];
879 [self postDataSourceChangedNotification];
882 - (void)deleteObject:(NSDictionary *)_obj {
883 NSString *whereClause = nil;
884 NSMutableString *expression = nil;
885 EOKeyGlobalID *gid = nil;
886 EOAdaptorChannel *adChan = nil;
890 if ((gid = [_obj valueForKey:@"globalID"]) == nil) {
891 [NSException raise:NSInvalidArgumentException
892 format:@"missing globalID, could not delete"];
894 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) {
895 NSDictionary *conD = nil;
897 conD = [(EOAdaptorGlobalID *)gid connectionDictionary];
898 if (![conD isEqualToDictionary:self->connectionDictionary]) {
899 [NSException raise:NSInvalidArgumentException
900 format:@"try to delete object %@ in wrong "
901 @"AdaptorDataSource %@", _obj, self];
903 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID];
906 if ([[self->adChannel adaptorContext] hasOpenTransaction])
911 adChan = [self beginTransaction];
912 whereClause = [self _whereClauseForGlobaID:gid
913 adaptor:[[adChan adaptorContext] adaptor] channel:adChan];
914 if (whereClause == nil) {
915 [self rollbackTransaction];
918 expression = [[NSMutableString alloc] initWithCapacity:256];
919 [expression appendString:@"DELETE FROM "];
920 [expression appendString:[gid entityName]];
921 [expression appendString:@" WHERE "];
922 [expression appendString:whereClause];
923 if (![adChan evaluateExpression:expression]) {
924 [adChan cancelFetch];
925 [self rollbackTransaction];
926 AUTORELEASE(expression);
927 [[[EOAdaptorException alloc]
928 initWithFormat:@"couldn`t evaluate expression %@ failed",
931 [adChan cancelFetch];
933 [self commitTransaction];
934 RELEASE(expression); expression = nil;
935 [self postDataSourceChangedNotification];
938 - (void)setFetchSpecification:(EOFetchSpecification *)_fs {
939 if (![self->fetchSpecification isEqual:_fs]) {
941 NSLog(@"%s: 0x%08X: fetch-spec mismatch:\n%@\n%@",
942 __PRETTY_FUNCTION__, self,
943 self->fetchSpecification, _fs);
946 ASSIGNCOPY(self->fetchSpecification, _fs);
948 [self postDataSourceItselfChangedNotification];
952 NSLog(@"%s: 0x%08X: no fetch-spec mismatch:\n%@\n%@\n",
953 __PRETTY_FUNCTION__, self,
954 self->fetchSpecification, _fs);
959 - (EOFetchSpecification *)fetchSpecification {
960 return [[self->fetchSpecification copy] autorelease];
963 - (NSString *)description {
964 return [NSString stringWithFormat:@"%@: adaptorChannel: %@",
965 [super description], self->adChannel];
968 @end /* EOAdaptorDataSource */
970 @implementation EOAdaptorDataSource(Private)
972 - (NSArray *)_primaryKeyAttributeNamesForTableName:(NSString *)_entityName
973 channel:(EOAdaptorChannel *)_adChannel
978 hints = [self->fetchSpecification hints];
979 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint];
983 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint];
986 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) {
987 attrs = [_adChannel attributesForTableName:_entityName];
991 attrs = [[attrs map:@selector(columnName)] map:@selector(lowercaseString)];
992 attrs = [attrs sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
997 - (NSArray *)_primaryKeyAttributesForTableName:(NSString *)_entityName
998 channel:(EOAdaptorChannel *)_adChannel
1001 NSDictionary *hints;
1003 hints = [self->fetchSpecification hints];
1005 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint];
1009 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint];
1012 NSEnumerator *enumerator;
1017 allAttrs = [_adChannel attributesForTableName:_entityName];
1018 objs = malloc(sizeof(id) * [allAttrs count]);
1019 enumerator = [allAttrs objectEnumerator];
1023 while ((obj = [enumerator nextObject])) {
1024 if ([attrs containsObject:[[obj columnName] lowercaseString]]) {
1025 objs[objCnt++] = obj;
1028 attrs = [NSArray arrayWithObjects:objs count:objCnt];
1029 free(objs); objs = NULL;
1032 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) {
1033 attrs = [_adChannel attributesForTableName:_entityName];
1038 - (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan {
1039 EOQualifier *qual = nil;
1040 NSArray *attrs = nil;
1041 NSString *entityName = nil;
1044 entityName = [self->fetchSpecification entityName];
1046 if ((attrs = self->__attributes) == nil)
1047 attrs = [_adChan attributesForTableName:entityName];
1049 if ((qual = self->__qualifier) == nil)
1050 qual = [self->fetchSpecification qualifier];
1055 adaptor = [[_adChan adaptorContext] adaptor];
1057 return [qual sqlExpressionWithAdaptor:adaptor attributes:attrs];
1060 - (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan {
1061 NSArray *attrs = nil;
1062 NSEnumerator *enumerator = nil;
1063 EOAttribute *attribute = nil;
1065 NSMutableString *select = nil;
1066 EOAdaptor *adaptor = nil;
1067 NSString *entityName = nil;
1069 adaptor = [[_adChan adaptorContext] adaptor];
1070 entityName = [self->fetchSpecification entityName];
1072 if ((attrs = self->__attributes) == nil)
1073 attrs = [_adChan attributesForTableName:entityName];
1075 attrs = [_adChan _sortAttributesForSelectExpression:attrs];
1076 select = [NSMutableString stringWithCapacity:128];
1077 enumerator = [attrs objectEnumerator];
1078 while ((attribute = [enumerator nextObject])) {
1082 [select appendString:@", "];
1084 [select appendString:[adaptor formatAttribute:attribute]];
1089 - (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid
1090 adaptor:(EOAdaptor *)_adaptor
1091 channel:(EOAdaptorChannel *)_adChan
1093 NSEnumerator *enumerator;
1094 NSMutableString *result;
1101 pKeys = [self _primaryKeyAttributeNamesForTableName:[_gid entityName]
1103 pkAttrs = [self _primaryKeyAttributesForTableName:[_gid entityName]
1107 if ([pKeys count] != [_gid keyCount]) {
1108 NSLog(@"ERROR[%s]: internal inconsitency pkeys %@ gid %@",
1109 __PRETTY_FUNCTION__, pKeys, _gid);
1112 enumerator = [pKeys objectEnumerator];
1116 while ((pKey = [enumerator nextObject])) {
1121 result = [NSMutableString stringWithCapacity:128];
1123 [result appendString:@" AND "];
1126 NSEnumerator *enumerator;
1128 enumerator = [pkAttrs objectEnumerator];
1129 while ((attr = [enumerator nextObject])) {
1130 if ([[[attr columnName] lowercaseString] isEqual:pKey])
1133 NSAssert2(attr != nil, @"missing attribute for pkName %@ attrs %@",
1136 [result appendString:[_adaptor formatAttribute:attr]];
1139 value = [(EOKeyGlobalID *)_gid keyValues][pkCnt++];
1140 if (value == nil) value = null;
1142 [result appendString:[value isNotNull] ? @"=" : @" IS "];
1143 [result appendString:[_adaptor formatValue:value forAttribute:attr]];
1148 - (NSDictionary *)_mapAttrsWithValues:(id)_keyValues
1149 tableName:(NSString *)_tableName
1150 channel:(EOAdaptorChannel *)_adChan
1156 NSDictionary *result;
1159 attrs = [_adChan attributesForTableName:_tableName];
1160 mapCnt = [attrs count];
1161 keys = calloc(mapCnt, sizeof(id));
1162 values = calloc(mapCnt, sizeof(id));
1163 en = [attrs objectEnumerator];
1166 while ((attr = [en nextObject])) {
1169 v = (v = [_keyValues valueForKey:[[attr columnName] lowercaseString]])
1172 keys[mapCnt] = attr;
1176 result = [[NSDictionary alloc]
1177 initWithObjects:values forKeys:keys count:mapCnt];
1178 free(keys); keys = NULL;
1179 free(values); values = NULL;
1180 return [result autorelease];
1183 @end /* EOAdaptorDataSource(Private) */
1185 @implementation EOAndQualifier(SqlExpression)
1187 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1188 attributes:(NSArray *)_attributes
1190 NSMutableString *str = nil;
1191 NSEnumerator *enumerator = nil;
1192 EOQualifier *qual = nil;
1194 NSString *result = nil;
1196 str = [[NSMutableString alloc] initWithCapacity:128];
1198 enumerator = [self->qualifiers objectEnumerator];
1199 while ((qual = [enumerator nextObject])) {
1202 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes];
1204 [str appendFormat:@"(%@)", s];
1208 [str appendFormat:@" AND (%@)", s];
1210 result = [str copy];
1211 [str release]; str = nil;
1212 return [result autorelease];
1214 @end /* EOAndQualifier(SqlExpression) */
1216 @implementation EOOrQualifier(SqlExpression)
1218 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1219 attributes:(NSArray *)_attributes
1221 NSMutableString *str = nil;
1222 NSEnumerator *enumerator = nil;
1223 EOQualifier *qual = nil;
1225 NSString *result = nil;
1227 str = [[NSMutableString alloc] initWithCapacity:128];
1229 enumerator = [self->qualifiers objectEnumerator];
1230 while ((qual = [enumerator nextObject])) {
1233 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes];
1235 [str appendFormat:@"(%@)", s];
1239 [str appendFormat:@" OR (%@)", s];
1241 result = [str copy];
1242 [str release]; str = nil;
1243 return [result autorelease];
1246 @end /* EOOrQualifier(SqlExpression) */
1248 @implementation EOKeyValueQualifier(SqlExpression)
1250 + (NSString *)sqlStringForOperatorSelector:(SEL)_sel {
1251 static NSMapTable *selectorToOperator = NULL;
1254 if ((s = NSStringFromSelector(_sel)) == nil)
1257 if (selectorToOperator == NULL) {
1258 selectorToOperator = NSCreateMapTable(NSObjectMapKeyCallBacks,
1259 NSObjectMapValueCallBacks,
1261 NSMapInsert(selectorToOperator,
1262 NSStringFromSelector(EOQualifierOperatorEqual),
1264 NSMapInsert(selectorToOperator,
1265 NSStringFromSelector(EOQualifierOperatorNotEqual),
1267 NSMapInsert(selectorToOperator,
1268 NSStringFromSelector(EOQualifierOperatorLessThan),
1270 NSMapInsert(selectorToOperator,
1271 NSStringFromSelector(EOQualifierOperatorGreaterThan),
1273 NSMapInsert(selectorToOperator,
1274 NSStringFromSelector(EOQualifierOperatorLessThanOrEqualTo),
1276 NSMapInsert(selectorToOperator,
1277 NSStringFromSelector(EOQualifierOperatorGreaterThanOrEqualTo),
1281 if ((ss = NSMapGet(selectorToOperator, s)))
1287 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1288 attributes:(NSArray *)_attributes
1290 EOAttribute *attr = nil;
1291 NSEnumerator *en = nil;
1293 NSString *sql = nil;
1294 NSString *sqlKey, *sqlValue;
1296 k = [self->key lowercaseString];
1297 en = [_attributes objectEnumerator];
1299 while ((attr = [en nextObject])) {
1300 if ([[[attr columnName] lowercaseString] isEqualToString:k]) {
1305 en = [_attributes objectEnumerator];
1306 while ((attr = [en nextObject])) {
1307 if ([[attr name] isEqualToString:self->key])
1312 NSLog(@"WARNING[%s]: missing attribute [%@] for qualifier %@",
1313 __PRETTY_FUNCTION__,
1318 sqlKey = [_adaptor formatAttribute:attr];
1320 sqlValue = [_adaptor formatValue:self->value ? self->value : null
1325 if (SEL_EQ(EOQualifierOperatorEqual, self->operator)) {
1326 if ([self->value isNotNull])
1327 sql = [NSString stringWithFormat:@"%@ = %@", sqlKey, sqlValue];
1329 sql = [NSString stringWithFormat:@"%@ IS NULL", sqlKey];
1331 else if (SEL_EQ(EOQualifierOperatorNotEqual, self->operator)) {
1332 if ([self->value isNotNull])
1333 sql = [NSString stringWithFormat:@"NOT (%@ = %@)", sqlKey, sqlValue];
1335 sql = [NSString stringWithFormat:@"%@ IS NOT NULL", sqlKey];
1337 else if (SEL_EQ(EOQualifierOperatorLessThan, self->operator)) {
1338 sql = [NSString stringWithFormat:@"%@ < %@", sqlKey, sqlValue];
1340 else if (SEL_EQ(EOQualifierOperatorLessThanOrEqualTo, self->operator)) {
1341 sql = [NSString stringWithFormat:@"%@ <= %@", sqlKey, sqlValue];
1343 else if (SEL_EQ(EOQualifierOperatorGreaterThan, self->operator)) {
1344 sql = [NSString stringWithFormat:@"%@ > %@", sqlKey, sqlValue];
1346 else if (SEL_EQ(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) {
1347 sql = [NSString stringWithFormat:@"%@ >= %@", sqlKey, sqlValue];
1349 else if (SEL_EQ(EOQualifierOperatorLike, self->operator)) {
1350 sqlValue = [[self->value stringValue]
1351 stringByReplacingString:@"*" withString:@"%"];
1352 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr];
1354 sql = [NSString stringWithFormat:@"%@ LIKE %@", sqlKey, sqlValue];
1356 else if (SEL_EQ(EOQualifierOperatorCaseInsensitiveLike, self->operator)) {
1357 sqlValue = [[self->value stringValue]
1358 stringByReplacingString:@"*" withString:@"%"];
1359 sqlValue = [sqlValue lowercaseString];
1360 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr];
1362 sql = [NSString stringWithFormat:@"LOWER(%@) LIKE %@", sqlKey, sqlValue];
1365 else if (SEL_EQ(EOQualifierOperatorLessThanOrEqualTo, self->operator)) {
1367 else if (SEL_EQ(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) {
1371 NSLog(@"ERROR(%s): unsupported SQL operator: %@", __PRETTY_FUNCTION__,
1372 [EOQualifier stringForOperatorSelector:self->operator]);
1373 [self notImplemented:_cmd];
1380 @end /* EOKeyValueQualifier(SqlExpression) */
1382 @implementation EONotQualifier(SqlExpression)
1384 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1385 attributes:(NSArray *)_attributes
1389 s = [self->qualifier sqlExpressionWithAdaptor:_adaptor
1390 attributes:_attributes];
1391 return [NSString stringWithFormat:@"NOT(%@)", s];
1394 @end /* EONotQualifier(SqlExpression) */
1396 @implementation EOKeyComparisonQualifier(SqlExpression)
1398 - (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor
1399 attributes:(NSArray *)_attributes
1401 NSLog(@"ERROR(%s): subclass needs to override this method!",
1402 __PRETTY_FUNCTION__);
1406 @end /* EOKeyComparisonQualifier(SqlExpression) */