1/* 2 EOAdaptorDataSource.m 3 4 Copyright (C) SKYRIX Software AG and Helge Hess 5 6 Date: 1999-2005 7 8 This file is part of the GNUstep Database Library. 9 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. 14 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. 19 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. 24*/ 25 26/* 27 column-names must have small letterso 28 hints: 29 EOPrimaryKeyAttributeNamesHint - name of primary key attributes 30 EOPrimaryKeyAttributesHint - primary key attributes 31 EOFetchResultTimeZone - NSTimeZone object for dates 32*/ 33 34#define EOAdaptorDataSource_DEBUG 0 35 36#include <NGExtensions/NGExtensions.h> 37#include <GDLAccess/GDLAccess.h> 38#include <EOControl/EOControl.h> 39#include "common.h" 40 41NSString *EOPrimaryKeyAttributeNamesHint = @"EOPrimaryKeyAttributeNamesHint"; 42NSString *EOPrimaryKeyAttributesHint = @"EOPrimaryKeyAttributesHint"; 43NSString *EOFetchResultTimeZone = @"EOFetchResultTimeZoneHint"; 44 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; 51@end 52 53static EONull *null = nil; 54 55@interface EOAdaptorChannel(Internals) 56- (NSArray *)_sortAttributesForSelectExpression:(NSArray *)_attrs; 57@end /* EOAdaptorChannel(Internals) */ 58 59@interface EOQualifier(SqlExpression) 60- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 61 attributes:(NSArray *)_attrs; 62@end /* EOQualifier(SqlExpression) */ 63 64@interface EOAdaptorDataSource(Private) 65 66- (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan; 67- (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan; 68- (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid 69 adaptor:(EOAdaptor *)_adaptor channel:(EOAdaptorChannel *)_adChan; 70 71- (NSString *)_orderByExprForAttributes:(NSArray *)_attrs 72 andPatchSelectList:(NSMutableString *)selectList 73 withChannel:(EOAdaptorChannel *)_adChan; 74 75- (NSDictionary *)_mapAttrsWithValues:(NSDictionary *)_keyValues 76 tableName:(NSString *)_tableName channel:(EOAdaptorChannel *)_adChan; 77 78@end /* EOAdaptorDataSource(Private) */ 79 80@interface EOAdaptorDataSource(Internals) 81- (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel 82 connectionDictionary:(NSDictionary *)_connDict; 83@end /* EOAdaptorDataSource(Internals) */ 84 85@interface EODataSource(Notificiations) 86- (void)postDataSourceChangedNotification; 87@end 88 89@implementation EOAdaptorDataSource 90 91static Class NSCalendarDateClass = nil; 92static NSNotificationCenter *nc = nil; 93 94static NSNotificationCenter *getNC(void ) { 95 if (nc == nil) 96 nc = [[NSNotificationCenter defaultCenter] retain]; 97 return nc; 98} 99 100+ (void)initialize { 101 null = [[EONull null] retain]; 102 NSCalendarDateClass = [NSCalendarDate class]; 103} 104 105- (id)initWithAdaptorName:(NSString *)_adName 106 connectionDictionary:(NSDictionary *)_dict 107 primaryKeyGenerationDictionary:(NSDictionary *)_pkGen 108{ 109 EOAdaptor *ad = nil; 110 EOAdaptorContext *ctx = nil; 111 EOAdaptorChannel *adc = nil; 112 113 ad = [EOAdaptor adaptorWithName:_adName]; 114 [ad setConnectionDictionary:_dict]; 115 [ad setPkeyGeneratorDictionary:_pkGen]; 116 ctx = [ad createAdaptorContext]; 117 adc = [ctx createAdaptorChannel]; 118 119 return [self initWithAdaptorChannel:adc connectionDictionary:_dict]; 120} 121 122- (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel { 123 return [self initWithAdaptorChannel:_channel connectionDictionary:nil]; 124} 125 126- (id)initWithAdaptorChannel:(EOAdaptorChannel *)_channel 127 connectionDictionary:(NSDictionary *)_connDict 128{ 129 if ((self = [super init])) { 130 self->adChannel = [_channel retain]; 131 self->connectionDictionary = [_connDict copy]; 132 self->commitTransaction = NO; 133 134 [getNC() 135 addObserver:self selector:@selector(_adDataSourceChanged:) 136 name:@"EOAdaptorDataSourceChanged" object:nil]; 137 } 138 return self; 139} 140 141- (void)dealloc { 142 [getNC() removeObserver:self]; 143 [self->fetchSpecification release]; 144 [self->connectionDictionary release]; 145 [self->adChannel release]; 146 [self->__attributes release]; 147 [self->__qualifier release]; 148 [super dealloc]; 149} 150 151/* notifications */ 152 153- (void)postDataSourceItselfChangedNotification { 154 [super postDataSourceChangedNotification]; 155} 156 157- (void)postDataSourceChangedNotification { 158 [getNC() postNotificationName:@"EOAdaptorDataSourceChanged" object:self]; 159 [self postDataSourceItselfChangedNotification]; 160} 161 162- (void)_adDataSourceChanged:(NSNotification *)_notification { 163 EOAdaptorDataSource *ads; 164 165 if ((ads = [_notification object]) == self) 166 return; 167 if (ads == nil) 168 return; 169 170 [self postDataSourceItselfChangedNotification]; 171 172#if 0 173 if (![ads->connectionDictionary isEqual:self->connectionDictionary]) 174 /* different database ... */ 175 return; 176 177 if ((ads->fetchSpecification == nil) || (self->fetchSpecification == nil)) { 178 [self postDataSourceChangedNotification]; 179 return; 180 } 181 182 /* check fspecs for entity ... */ 183 if ([[ads->fetchSpecification entityName] 184 isEqualToString: 185 [self->fetchSpecification entityName]]) { 186 [self postDataSourceChangedNotification]; 187 return; 188 } 189#endif 190} 191 192/* fetching */ 193 194- (EOAdaptorChannel *)beginTransaction { 195 EOAdaptorContext *ctx = nil; 196 197 [self openChannel]; 198 ctx = [self->adChannel adaptorContext]; 199 if ([ctx hasOpenTransaction] == NO) { 200 [ctx beginTransaction]; 201 self->commitTransaction = YES; 202 } 203 return self->adChannel; 204} 205 206- (void)commitTransaction { 207 if (self->commitTransaction) { 208 [[self->adChannel adaptorContext] commitTransaction]; 209 // [self->adChannel closeChannel]; 210 self->commitTransaction = NO; 211 } 212} 213 214- (void)rollbackTransaction { 215 [[self->adChannel adaptorContext] rollbackTransaction]; 216 // [self->adChannel closeChannel]; 217 self->commitTransaction = NO; 218} 219 220- (void)openChannel { 221 if (![self->adChannel isOpen]) { 222 [self->adChannel openChannel]; 223 } 224} 225 226- (void)closeChannel { 227 if (![self->adChannel isOpen]) 228 return; 229 230 if ([[self->adChannel adaptorContext] transactionNestingLevel]) { 231 NSLog(@"%s was called while transaction in progress, rollback will called", 232 __PRETTY_FUNCTION__); 233 [self rollbackTransaction]; 234 } 235 [self->adChannel closeChannel]; 236} 237 238- (NSArray *)fetchObjects { 239 // TODO: split up this HUGE method! 240 // TODO: all the SQL gen code should be moved to an appropriate object 241 NSString *entityName = nil; 242 NSString *whereExpr = nil; 243 NSString *orderByExpr = nil; 244 NSMutableString *selectList = nil; 245 NSMutableString *expression = nil; 246 NSMutableArray *result = nil; 247 NSArray *attrs = nil; 248 NSArray *pKeys = nil; 249 EOQualifier *qual = nil; 250 EOAdaptorChannel *adChan = nil; 251 int pKeyCnt = 0; 252 NSTimeZone *tz = nil; 253 BOOL localComTrans; 254 255 if (self->fetchSpecification == nil) { 256 // TODO: make that a lastException and just return nil 257 [NSException raise:NSInvalidArgumentException 258 format:@"fetchSpecification required for table name"]; 259 return nil; 260 } 261 262 entityName = [self->fetchSpecification entityName]; 263 264 if (entityName == nil || [entityName length] == 0) { 265 [NSException raise:NSInvalidArgumentException 266 format:@"missing entity name"]; 267 } 268 localComTrans = [[self->adChannel adaptorContext] hasOpenTransaction] 269 ? NO : YES; 270 271 adChan = [self beginTransaction]; 272 pKeys = [self _primaryKeyAttributeNamesForTableName:entityName 273 channel:adChan]; 274 275 if ((pKeyCnt = [pKeys count]) == 0) { 276 NSLog(@"ERROR[%s]: missing primary keys for table %@", 277 __PRETTY_FUNCTION__, entityName); 278 return nil; 279 } 280 qual = [self->fetchSpecification qualifier]; 281 282 if (qual == nil) 283 qual = [EOQualifier qualifierWithQualifierFormat:@"1=1"]; 284 285 ASSIGN(self->__qualifier, qual); 286 287 attrs = [adChan attributesForTableName:entityName]; 288 289 if (attrs == nil) { 290 RELEASE(self->__qualifier); self->__qualifier = nil; 291 292 NSLog(@"ERROR[%s]: could not find table '%@' in database.", 293 __PRETTY_FUNCTION__, entityName); 294 [self rollbackTransaction]; 295 return nil; 296 } 297 if ([attrs count] == 0) { 298 RELEASE(self->__qualifier); self->__qualifier = nil; 299 300 NSLog(@"ERROR[%s]: missing columns in table '%@'.", 301 __PRETTY_FUNCTION__, entityName); 302 [self rollbackTransaction]; 303 return nil; 304 } 305 tz = [[self->fetchSpecification hints] objectForKey:EOFetchResultTimeZone]; 306 307 ASSIGN(self->__attributes, attrs); 308 { 309 NSArray *a; 310 NSSet *tableKeys = nil; 311 NSSet *qualifierKeys = nil; 312 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]; 317 318 if ([qualifierKeys isSubsetOfSet:tableKeys] == NO) { 319 NSString *format = nil; 320 321 format = [NSString stringWithFormat: 322 @"EOAdaptorDataSource: using unmapped key in " 323 @"qualifier tableKeys <%@> qualifierKeys <%@> " 324 @"qualifier <%@>", 325 tableKeys, qualifierKeys, qual]; 326 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]; 332 } 333 RELEASE(tableKeys); tableKeys = nil; 334 RELEASE(qualifierKeys); qualifierKeys = nil; 335 } 336 337 whereExpr = [self _whereExprWithChannel:adChan]; 338 selectList = [self _selectListWithChannel:adChan]; 339 orderByExpr = [self _orderByExprForAttributes:attrs 340 andPatchSelectList:selectList 341 withChannel:adChan]; 342 343 expression = [[NSMutableString alloc] initWithCapacity:256]; 344 [expression appendString:@"SELECT "]; 345 346 if ([self->fetchSpecification usesDistinct]) 347 [expression appendString:@"DISTINCT "]; 348 349 [expression appendString:selectList]; 350 [expression appendString:@" FROM "]; 351 [expression appendString:entityName]; 352 if ([whereExpr length] > 0) { 353 [expression appendString:@" WHERE "]; 354 [expression appendString:whereExpr]; 355 } 356 if (orderByExpr != nil && [orderByExpr length] > 0) { 357 [expression appendString:@" ORDER BY "]; 358 [expression appendString:orderByExpr]; 359 } 360 361 if (![adChan evaluateExpression:expression]) { 362 RELEASE(self->__attributes); self->__attributes = nil; 363 RELEASE(self->__qualifier); self->__qualifier = nil; 364 AUTORELEASE(expression); 365 [adChan cancelFetch]; 366 [self rollbackTransaction]; 367 [[[EOAdaptorException alloc] 368 initWithFormat:@"evaluateExpression of %@ failed", expression] raise]; 369 } 370 result = [NSMutableArray arrayWithCapacity:64]; 371 { 372 NSMutableDictionary *row = nil; 373 unsigned fetchCnt = 0; 374 unsigned fetchLimit = 0; 375 unsigned attrCnt = 0; 376 id *values = NULL; 377 id *keys = NULL; 378 379 /* Note: those are reused in the inner loop */ 380 attrCnt = [attrs count]; 381 values = calloc(attrCnt + 2, sizeof(id)); 382 keys = calloc(attrCnt + 2, sizeof(id)); 383 fetchLimit = [self->fetchSpecification fetchLimit]; 384 385 while ((row = [adChan fetchAttributes:attrs withZone:NULL]) != nil) { 386 NSEnumerator *enumerator = nil; 387 id attr = nil; 388 int rowCnt = 0; 389 NSDictionary *r = nil; 390 id *pKeyVs = NULL; 391 int pKeyVCnt = 0; 392 393 pKeyVs = calloc(pKeyCnt, sizeof(id)); 394 enumerator = [attrs objectEnumerator]; 395 396 while ((attr = [enumerator nextObject]) != nil) { 397 id obj; 398 NSString *cn; 399 400 obj = [row objectForKey:[(EOAttribute *)attr name]]; 401 402 if (obj == nil) 403 continue; 404 405 if (tz != nil && [obj isKindOfClass:NSCalendarDateClass]) 406 [obj setTimeZone:tz]; 407 408 cn = [[attr columnName] lowercaseString]; 409 values[rowCnt] = obj; 410 keys[rowCnt] = cn; 411 rowCnt++; 412 413 if ([pKeys containsObject:cn]) { 414 int idx; 415 416 idx = [pKeys indexOfObject:cn]; 417 NSAssert4(idx <= (pKeyCnt - 1) && pKeyVs[idx] == nil, 418 @"internal inconsistency in EOAdaptorDataSource " 419 @"while fetch idx[%d] > (pKeyCnt - 1)[%d] " 420 @"pKeyVs[idx] (%@[%d]);", idx, (pKeyCnt - 1), 421 pKeyVs[idx], idx); 422 423 pKeyVs[idx] = obj; 424 pKeyVCnt++; 425 } 426 } 427 if (pKeyCnt != pKeyVCnt) 428 NSAssert(NO, @"internal inconsistency in EOAdaptorDataSource " 429 @"while fetch"); 430 431 { 432 EOGlobalID *gid; 433 434 gid = [EOKeyGlobalID globalIDWithEntityName:entityName 435 keys:pKeyVs keyCount:pKeyVCnt zone:NULL]; 436 437 if (self->connectionDictionary) { 438 gid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid 439 connectionDictionary: 440 self->connectionDictionary]; 441 AUTORELEASE(gid); 442 } 443 values[rowCnt] = gid; 444 keys[rowCnt] = @"globalID"; 445 rowCnt++; 446 } 447 fetchCnt++; 448 r = [[NSMutableDictionary alloc] 449 initWithObjects:values forKeys:keys count:rowCnt]; 450 [result addObject:r]; 451 [r release]; r = nil; 452 if (pKeyVs) free(pKeyVs); pKeyVs = NULL; 453 if (fetchLimit == fetchCnt) 454 break; 455 } 456 if (values) free(values); values = NULL; 457 if (keys) free(keys); keys = NULL; 458 } 459 [adChan cancelFetch]; 460 if (localComTrans) 461 [self commitTransaction]; 462 463 [expression release]; expression = nil; 464 [self->__qualifier release]; self->__qualifier = nil; 465 [self->__attributes release]; self->__attributes = nil; 466 return result; 467} 468 469- (id)createObject { 470 return [NSMutableDictionary dictionary]; 471} 472 473- (void)insertObject:(id)_obj { 474 NSString *key = nil; 475 NSString *tableName = nil; 476 NSMutableString *expression = nil; 477 EOAdaptor *adaptor = nil; 478 NSArray *pKeys = nil; 479 id obj = nil; 480 EOAdaptorChannel *adChan = nil; 481 482 int oVCnt = 0; 483 NSString **objectKeyAttrValue = NULL; 484 485 NSEnumerator *enumerator = nil; 486 id pKey = nil; 487 488 BOOL localComTrans; 489 490 if ([[self->adChannel adaptorContext] hasOpenTransaction]) 491 localComTrans = NO; 492 else 493 localComTrans = YES; 494 495 adChan = [self beginTransaction]; 496 adaptor = [[adChan adaptorContext] adaptor]; 497 498 if ((tableName = [self->fetchSpecification entityName]) == nil) { 499 [self rollbackTransaction]; 500 [NSException raise:NSInvalidArgumentException 501 format:@"couldn`t insert obj %@ missing entityName in " 502 @"fetchSpecification", _obj]; 503 } 504 505 /* create or apply primary keys */ 506#if EOAdaptorDataSource_DEBUG 507 NSLog(@"insert obj %@", _obj); 508#endif 509 510 pKeys = [self _primaryKeyAttributeNamesForTableName:tableName channel:adChan]; 511 512#if EOAdaptorDataSource_DEBUG 513 NSLog(@"got primary keys %@", pKeys); 514#endif 515 516 objectKeyAttrValue = calloc([pKeys count], sizeof(id)); 517 enumerator = [pKeys objectEnumerator]; 518 519 while ((pKey = [enumerator nextObject])) { 520 id pKeyObj; 521 522 pKeyObj = [_obj valueForKey:pKey]; 523 524#if EOAdaptorDataSource_DEBUG 525 NSLog(@"pk in obj %@:<%@> ", pKey, pKeyObj); 526#endif 527 528 if (![pKeyObj isNotNull]) { 529 /* try to build primary key */ 530 NSString *newKeyExpr = nil; 531 NSDictionary *row = nil; 532 533#if EOAdaptorDataSource_DEBUG 534 NSLog(@"pKeyObj !isNotNull"); 535#endif 536 537 if ([pKeys count] != 1) { 538 [self rollbackTransaction]; 539 [NSException raise:NSInternalInconsistencyException 540 format:@"more than one primary key, " 541 @"and primary key for %@ is not set", pKey]; 542 } 543 if (![adaptor respondsToSelector:@selector(newKeyExpression)]) { 544 [self rollbackTransaction]; 545 [NSException raise:NSInternalInconsistencyException 546 format:@"got no newkey expression, insert failed"]; 547 } 548 newKeyExpr = [(id)adaptor newKeyExpression]; 549 if (newKeyExpr == nil) { 550 [self rollbackTransaction]; 551 [NSException raise:NSInternalInconsistencyException 552 format:@"missing newKeyExpression for adaptor[%@] %@...", 553 NSStringFromClass([adaptor class]), adaptor]; 554 } 555 if (![adChan evaluateExpression:newKeyExpr]) { 556 [adChan cancelFetch]; 557 [self rollbackTransaction]; 558 [[[EOAdaptorException alloc] 559 initWithFormat:@"couldn`t evaluate new key expression %@", 560 newKeyExpr] raise]; 561 } 562 row = [adChan fetchAttributes:[adChan describeResults] 563 withZone:NULL]; 564 [adChan cancelFetch]; 565 if ((key = [[row objectEnumerator] nextObject]) == nil) { 566 [self rollbackTransaction]; 567 [[[EOAdaptorException alloc] 568 initWithFormat:@"couldn`t fetch primary key"] raise];; 569 } 570 objectKeyAttrValue[oVCnt++] = key; 571 [_obj takeValue:key forKey:pKey]; 572#if EOAdaptorDataSource_DEBUG 573 NSLog(@"_obj %@ after takeValue:%@ forKey:%@", _obj, key, pKey); 574#endif 575 } 576 else { 577 objectKeyAttrValue[oVCnt++] = pKeyObj; 578#if EOAdaptorDataSource_DEBUG 579 NSLog(@"objectKeyAttrValue takeValue %@ for idx %d", pKeyObj, oVCnt); 580#endif 581 } 582 } 583 584 /* construct SQL INSERT expression .. */ 585 586 expression = [[NSMutableString alloc] initWithCapacity:256]; 587 [expression appendString:@"INSERT INTO "]; 588 [expression appendString:tableName]; 589 [expression appendString:@"("]; 590 { 591 NSDictionary *objects = nil; 592 NSEnumerator *enumerator = nil; 593 NSArray *allKeys = nil; 594 BOOL isFirst = YES; 595 596 objects = [self _mapAttrsWithValues:_obj 597 tableName:tableName 598 channel:adChan]; 599 allKeys = [objects allKeys]; 600 enumerator = [allKeys objectEnumerator]; 601 while ((obj = [enumerator nextObject])) { 602 if (isFirst) 603 isFirst = NO; 604 else 605 [expression appendString:@", "]; 606 [expression appendString:[adaptor formatAttribute:obj]]; 607 } 608 [expression appendString:@") VALUES ("]; 609 enumerator = [allKeys objectEnumerator]; 610 isFirst = YES; 611 while ((obj = [enumerator nextObject])) { 612 id value; 613 614 if (isFirst) 615 isFirst = NO; 616 else 617 [expression appendString:@", "]; 618 value = [objects objectForKey:obj]; 619 if (value == nil) value = null; 620 [expression appendString:[adaptor formatValue:value forAttribute:obj]]; 621 } 622 } 623 [expression appendString:@")"]; 624 625 /* execute insert in SQL server .. */ 626 627 if (![adChan evaluateExpression:expression]) { 628 [adChan cancelFetch]; 629 enumerator = [pKeys objectEnumerator]; 630 while ((pKey = [enumerator nextObject])) { 631 [_obj takeValue:[EONull null] forKey:pKey]; 632 } 633 [self rollbackTransaction]; 634 AUTORELEASE(expression); 635 [[[EOAdaptorException alloc] 636 initWithFormat:@"evaluateExpression %@ failed", expression] raise]; 637 } 638 [adChan cancelFetch]; 639 if (localComTrans) 640 [self commitTransaction]; 641 642 /* construct new global id for record */ 643 644 { 645 EOGlobalID *gid; 646 647 gid = [EOKeyGlobalID globalIDWithEntityName:tableName 648 keys:objectKeyAttrValue keyCount:oVCnt zone:NULL]; 649 if (self->connectionDictionary != nil) { 650 EOAdaptorGlobalID *agid = nil; 651 652 agid = [[EOAdaptorGlobalID alloc] initWithGlobalID:gid 653 connectionDictionary: 654 self->connectionDictionary]; 655 AUTORELEASE(agid); 656 gid = agid; 657 } 658 [_obj takeValue:gid forKey:@"globalID"]; 659 } 660 661 RELEASE(expression); expression = NULL; 662 663 /* mark datasource as changed */ 664 665 [self postDataSourceChangedNotification]; 666} 667 668- (void)updateObject:(id)_obj { 669 NSString *whereClause = nil; 670 NSMutableString *expression = nil; 671 EOAdaptor *adaptor = nil; 672 EOKeyGlobalID *gid = nil; 673 NSEnumerator *enumerator = nil; 674 NSString *tableName = nil; 675 EOAttribute *attr = nil; 676 BOOL isFirst = YES; 677 NSDictionary *objects = nil; 678 EOAdaptorChannel *adChan = nil; 679 680 BOOL localComTrans; 681 682 if ((gid = [_obj valueForKey:@"globalID"]) == nil) { 683 [NSException raise:NSInvalidArgumentException 684 format:@"missing globalID, couldn`t update"]; 685 } 686 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) { 687 NSDictionary *conD = nil; 688 689 conD = [(EOAdaptorGlobalID *)gid connectionDictionary]; 690 if (![conD isEqualToDictionary:self->connectionDictionary]) { 691 [NSException raise:NSInvalidArgumentException 692 format:@"try to update object %@ in " 693 @"wrong AdaptorDataSource %@", _obj, self]; 694 } 695 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID]; 696 } 697 if ([[self->adChannel adaptorContext] hasOpenTransaction]) 698 localComTrans = NO; 699 else 700 localComTrans = YES; 701 702 adChan = [self beginTransaction]; 703 tableName = [gid entityName]; 704 adaptor = [[adChan adaptorContext] adaptor]; 705 whereClause = [self _whereClauseForGlobaID:gid adaptor:adaptor 706 channel:adChan]; 707 if (whereClause == nil) { 708 [self rollbackTransaction]; 709 return; 710 } 711 expression = [[NSMutableString alloc] initWithCapacity:256]; 712 [expression appendString:@"UPDATE "]; 713 [expression appendString:[gid entityName]]; 714 [expression appendString:@" SET "]; 715 716 objects = [self _mapAttrsWithValues:_obj tableName:tableName 717 channel:adChan]; 718 enumerator = [objects keyEnumerator]; 719 720 while ((attr = [enumerator nextObject])) { 721 id value; 722 723 if (isFirst) 724 isFirst = NO; 725 else 726 [expression appendString:@", "]; 727 [expression appendString:[adaptor formatAttribute:attr]]; 728 [expression appendString:@"="]; 729 730 value = [objects objectForKey:attr]; 731 if (value == nil) value = null; 732 733 [expression appendString:[adaptor formatValue:value forAttribute:attr]]; 734 } 735 [expression appendString:@" WHERE "]; 736 [expression appendString:whereClause]; 737 if (![adChan evaluateExpression:expression]) { 738 [adChan cancelFetch]; 739 [self rollbackTransaction]; 740 AUTORELEASE(expression); 741 [[[EOAdaptorException alloc] 742 initWithFormat:@"evaluate expression %@ failed", expression] raise]; 743 } 744 [adChan cancelFetch]; 745 if (localComTrans) 746 [self commitTransaction]; 747 748 RELEASE(expression); expression = nil; 749 750 { 751 EOGlobalID *newGID; 752 NSArray *attrs; 753 NSEnumerator *enumerator; 754 id *objs; 755 int objCnt; 756 NSString *attr; 757 758 attrs = [self _primaryKeyAttributeNamesForTableName:[gid entityName] 759 channel:adChan]; 760 enumerator = [attrs objectEnumerator]; 761 objCnt = 0; 762 objs = calloc([attrs count], sizeof(id)); 763 764 while ((attr = [enumerator nextObject])) { 765 objs[objCnt] = [_obj valueForKey:attr]; 766 objCnt++; 767 } 768 newGID = [EOKeyGlobalID globalIDWithEntityName:[gid entityName] 769 keys:objs keyCount:objCnt zone:NULL]; 770 if (self->connectionDictionary != nil) { 771 newGID = [[EOAdaptorGlobalID alloc] initWithGlobalID:newGID 772 connectionDictionary: 773 self->connectionDictionary]; 774 [newGID autorelease]; 775 } 776 [(NSMutableDictionary *)_obj setObject:newGID forKey:@"globalID"]; 777 } 778 [self postDataSourceChangedNotification]; 779} 780 781- (void)deleteObject:(id)_obj { 782 NSString *whereClause = nil; 783 NSMutableString *expression = nil; 784 EOKeyGlobalID *gid = nil; 785 EOAdaptorChannel *adChan = nil; 786 787 BOOL localComTrans; 788 789 if ((gid = [_obj valueForKey:@"globalID"]) == nil) { 790 [NSException raise:NSInvalidArgumentException 791 format:@"missing globalID, could not delete"]; 792 } 793 if ([gid isKindOfClass:[EOAdaptorGlobalID class]]) { 794 NSDictionary *conD = nil; 795 796 conD = [(EOAdaptorGlobalID *)gid connectionDictionary]; 797 if (![conD isEqualToDictionary:self->connectionDictionary]) { 798 [NSException raise:NSInvalidArgumentException 799 format:@"try to delete object %@ in wrong " 800 @"AdaptorDataSource %@", _obj, self]; 801 } 802 gid = (EOKeyGlobalID *)[(EOAdaptorGlobalID *)gid globalID]; 803 } 804 805 if ([[self->adChannel adaptorContext] hasOpenTransaction]) 806 localComTrans = NO; 807 else 808 localComTrans = YES; 809 810 adChan = [self beginTransaction]; 811 whereClause = [self _whereClauseForGlobaID:gid 812 adaptor:[[adChan adaptorContext] adaptor] channel:adChan]; 813 if (whereClause == nil) { 814 [self rollbackTransaction]; 815 return; 816 } 817 expression = [[NSMutableString alloc] initWithCapacity:256]; 818 [expression appendString:@"DELETE FROM "]; 819 [expression appendString:[gid entityName]]; 820 [expression appendString:@" WHERE "]; 821 [expression appendString:whereClause]; 822 if (![adChan evaluateExpression:expression]) { 823 [adChan cancelFetch]; 824 [self rollbackTransaction]; 825 AUTORELEASE(expression); 826 [[[EOAdaptorException alloc] 827 initWithFormat:@"couldn`t evaluate expression %@ failed", 828 expression] raise]; 829 } 830 [adChan cancelFetch]; 831 if (localComTrans) 832 [self commitTransaction]; 833 RELEASE(expression); expression = nil; 834 [self postDataSourceChangedNotification]; 835} 836 837- (void)setFetchSpecification:(EOFetchSpecification *)_fs { 838 if (![self->fetchSpecification isEqual:_fs]) { 839#if DEBUG && 0 840 NSLog(@"%s: 0x%p: fetch-spec mismatch:\n%@\n%@", 841 __PRETTY_FUNCTION__, self, 842 self->fetchSpecification, _fs); 843#endif 844 845 ASSIGNCOPY(self->fetchSpecification, _fs); 846 847 [self postDataSourceItselfChangedNotification]; 848 } 849#if DEBUG && 0 850 else { 851 NSLog(@"%s: 0x%p: no fetch-spec mismatch:\n%@\n%@\n", 852 __PRETTY_FUNCTION__, self, 853 self->fetchSpecification, _fs); 854 } 855#endif 856} 857 858- (EOFetchSpecification *)fetchSpecification { 859 /* 860 Note: the copy is intended, since the fetchspec is mutable, the consumer 861 could otherwise modify it "behind the scenes" 862 */ 863 return [[self->fetchSpecification copy] autorelease]; 864} 865 866/* description */ 867 868- (NSString *)description { 869 NSMutableString *ms; 870 871 ms = [NSMutableString stringWithCapacity:128]; 872 [ms appendFormat:@"<%@[0x%p]:", NSStringFromClass([self class]), self]; 873 874 if (self->fetchSpecification != nil) 875 [ms appendFormat:@" fspec=%@", self->fetchSpecification]; 876 if (self->adChannel != nil) 877 [ms appendFormat:@" channel=%@", self->adChannel]; 878 879 [ms appendString:@">"]; 880 return ms; 881} 882 883@end /* EOAdaptorDataSource */ 884 885@implementation EOAdaptorDataSource(Private) 886 887- (NSArray *)_primaryKeyAttributeNamesForTableName:(NSString *)_entityName 888 channel:(EOAdaptorChannel *)_adChannel 889{ 890 NSDictionary *hints; 891 NSArray *attrs; 892 893 hints = [self->fetchSpecification hints]; 894 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint]; 895 if (attrs) 896 return attrs; 897 898 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint]; 899 900 if (attrs == nil) { 901 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) { 902 attrs = [_adChannel attributesForTableName:_entityName]; 903 } 904 } 905 906 attrs = [[attrs map:@selector(columnName)] map:@selector(lowercaseString)]; 907 attrs = [attrs sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 908 909 return attrs; 910} 911 912- (NSArray *)_primaryKeyAttributesForTableName:(NSString *)_entityName 913 channel:(EOAdaptorChannel *)_adChannel 914{ 915 NSArray *attrs; 916 NSDictionary *hints; 917 918 hints = [self->fetchSpecification hints]; 919 920 attrs = [hints objectForKey:EOPrimaryKeyAttributesHint]; 921 if (attrs != nil) 922 return attrs; 923 924 attrs = [hints objectForKey:EOPrimaryKeyAttributeNamesHint]; 925 if (attrs != nil) { 926 NSArray *allAttrs; 927 NSEnumerator *enumerator; 928 id *objs; 929 int objCnt; 930 id obj; 931 932 allAttrs = [_adChannel attributesForTableName:_entityName]; 933 objs = malloc(sizeof(id) * [allAttrs count]); 934 enumerator = [allAttrs objectEnumerator]; 935 936 objCnt = 0; 937 938 while ((obj = [enumerator nextObject])) { 939 if ([attrs containsObject:[[obj columnName] lowercaseString]]) { 940 objs[objCnt++] = obj; 941 } 942 } 943 attrs = [NSArray arrayWithObjects:objs count:objCnt]; 944 free(objs); objs = NULL; 945 return attrs; 946 } 947 if (!(attrs = [_adChannel primaryKeyAttributesForTableName:_entityName])) { 948 attrs = [_adChannel attributesForTableName:_entityName]; 949 } 950 return attrs; 951} 952 953- (NSString *)_whereExprWithChannel:(EOAdaptorChannel *)_adChan { 954 EOQualifier *qual = nil; 955 NSArray *attrs = nil; 956 NSString *entityName = nil; 957 EOAdaptor *adaptor; 958 959 entityName = [self->fetchSpecification entityName]; 960 961 if ((attrs = self->__attributes) == nil) 962 attrs = [_adChan attributesForTableName:entityName]; 963 964 if ((qual = self->__qualifier) == nil) 965 qual = [self->fetchSpecification qualifier]; 966 967 if (qual == nil) 968 return nil; 969 970 adaptor = [[_adChan adaptorContext] adaptor]; 971 972 return [qual sqlExpressionWithAdaptor:adaptor attributes:attrs]; 973} 974 975- (NSException *)_couldNotFindSortAttributeInAttributes:(NSArray *)_attrs 976 forSortOrdering:(EOSortOrdering *)_so 977{ 978 return [[InvalidAttributeException alloc] 979 initWithFormat:@"could not find EOAttribute for SortOrdering" 980 @" %@ Attributes %@", _so, _attrs]; 981} 982 983- (EOAttribute *)findAttributeForKey:(NSString *)key 984 inAttributes:(NSArray *)_attrs 985{ 986 NSEnumerator *en; 987 EOAttribute *obj; 988 989 key = [key lowercaseString]; 990 en = [_attrs objectEnumerator]; 991 while ((obj = [en nextObject]) != nil) { 992 if ([[[obj columnName] lowercaseString] isEqualToString:key]) 993 break; 994 } 995 return obj; 996} 997 998- (NSString *)_orderByExprForAttributes:(NSArray *)_attrs 999 andPatchSelectList:(NSMutableString *)selectList 1000 withChannel:(EOAdaptorChannel *)_adChan 1001{ 1002 NSMutableString *orderByExpr; 1003 NSEnumerator *enumerator = nil; 1004 EOSortOrdering *sortOrdering = nil; 1005 int orderCnt = 0; 1006 EOAdaptor *adaptor; 1007 1008 adaptor = [[_adChan adaptorContext] adaptor]; 1009 1010 orderByExpr = nil; 1011 enumerator = [[self->fetchSpecification sortOrderings] objectEnumerator]; 1012 while ((sortOrdering = [enumerator nextObject]) != nil) { 1013 SEL selector = NULL; 1014 NSString *key = nil; 1015 EOAttribute *keyAttr = nil; 1016 int order = 0; /* 0 - not used; 1 - asc; 2 - desc */ 1017 BOOL inSensitive = NO; 1018 NSString *orderTmp = nil; 1019 1020 if (orderByExpr == nil) 1021 orderByExpr = [NSMutableString stringWithCapacity:64]; 1022 else 1023 [orderByExpr appendString:@", "]; 1024 1025 if ((selector = [sortOrdering selector])) { 1026 if (sel_isEqual(selector, EOCompareAscending)) 1027 order = 1; 1028 else if (sel_isEqual(selector, EOCompareDescending)) 1029 order = 2; 1030 else if (sel_isEqual(selector, EOCompareCaseInsensitiveAscending)) { 1031 order = 1; 1032 inSensitive = YES; 1033 } 1034 else if (sel_isEqual(selector, EOCompareCaseInsensitiveDescending)) { 1035 order = 2; 1036 inSensitive = YES; 1037 } 1038 } 1039 key = [sortOrdering key]; 1040 1041 if (key == nil || [key length] == 0) { 1042 NSLog(@"WARNING[%s]: no key in sortordering %@", 1043 __PRETTY_FUNCTION__, key); 1044 continue; 1045 } 1046 { 1047 EOAttribute *obj; 1048 1049 key = [key lowercaseString]; 1050 obj = [self findAttributeForKey:key inAttributes:_attrs]; 1051 if (obj == nil) { 1052 [self->__attributes release]; self->__attributes = nil; 1053 [self->__qualifier release]; self->__qualifier = nil; 1054#if 0 // TODO: memleak in error case 1055 [expression release]; expression = nil; 1056#endif 1057 [self rollbackTransaction]; 1058 1059 [[self _couldNotFindSortAttributeInAttributes:_attrs 1060 forSortOrdering:sortOrdering] raise]; 1061 return nil; 1062 } 1063 1064 keyAttr = obj; 1065 } 1066 key = [adaptor formatAttribute:keyAttr]; 1067 orderTmp = [NSString stringWithFormat:@"order%d", orderCnt]; 1068 orderCnt++; 1069 [orderByExpr appendString:orderTmp]; 1070 if (order == 1) 1071 [orderByExpr appendString:@" ASC"]; 1072 else if (order == 2) 1073 [orderByExpr appendString:@" DESC"]; 1074 1075 /* manipulate select expr */ 1076 if (inSensitive) { 1077 if ([[keyAttr valueClassName] isEqualToString:@"NSString"]) { 1078 key = [NSString stringWithFormat:@"LOWER(%@)", key]; 1079 } 1080 else 1081 NSLog(@"WARNING[%s]: inSensitive expression for no text attribute", 1082 __PRETTY_FUNCTION__); 1083 } 1084 { 1085 NSString *str = nil; 1086 1087 str = [key stringByAppendingString:@" AS "]; 1088 str = [str stringByAppendingString:orderTmp]; 1089 str = [str stringByAppendingString:@", "]; 1090 1091 [selectList insertString:str atIndex:0]; 1092 } 1093 } 1094 return orderByExpr; 1095} 1096 1097- (NSMutableString *)_selectListWithChannel:(EOAdaptorChannel *)_adChan { 1098 NSArray *attrs = nil; 1099 NSEnumerator *enumerator = nil; 1100 EOAttribute *attribute = nil; 1101 BOOL first = YES; 1102 NSMutableString *select = nil; 1103 EOAdaptor *adaptor = nil; 1104 NSString *entityName = nil; 1105 1106 adaptor = [[_adChan adaptorContext] adaptor]; 1107 entityName = [self->fetchSpecification entityName]; 1108 1109 if ((attrs = self->__attributes) == nil) 1110 attrs = [_adChan attributesForTableName:entityName]; 1111 1112 attrs = [_adChan _sortAttributesForSelectExpression:attrs]; 1113 select = [NSMutableString stringWithCapacity:128]; 1114 enumerator = [attrs objectEnumerator]; 1115 while ((attribute = [enumerator nextObject])) { 1116 if (first) 1117 first = NO; 1118 else 1119 [select appendString:@", "]; 1120 1121 [select appendString:[adaptor formatAttribute:attribute]]; 1122 } 1123 return select; 1124} 1125 1126- (NSString *)_whereClauseForGlobaID:(EOKeyGlobalID *)_gid 1127 adaptor:(EOAdaptor *)_adaptor 1128 channel:(EOAdaptorChannel *)_adChan 1129{ 1130 NSEnumerator *enumerator; 1131 NSMutableString *result; 1132 NSArray *pKeys; 1133 NSArray *pkAttrs; 1134 NSString *pKey; 1135 int pkCnt; 1136 1137 1138 pKeys = [self _primaryKeyAttributeNamesForTableName:[_gid entityName] 1139 channel:_adChan]; 1140 pkAttrs = [self _primaryKeyAttributesForTableName:[_gid entityName] 1141 channel:_adChan]; 1142 1143 1144 if ([pKeys count] != [_gid keyCount]) { 1145 NSLog(@"ERROR[%s]: internal inconsitency pkeys %@ gid %@", 1146 __PRETTY_FUNCTION__, pKeys, _gid); 1147 return nil; 1148 } 1149 enumerator = [pKeys objectEnumerator]; 1150 1151 pkCnt = 0; 1152 result = nil; 1153 while ((pKey = [enumerator nextObject])) { 1154 EOAttribute *attr; 1155 id value; 1156 1157 if (result == nil) 1158 result = [NSMutableString stringWithCapacity:128]; 1159 else 1160 [result appendString:@" AND "]; 1161 1162 { 1163 NSEnumerator *enumerator; 1164 1165 enumerator = [pkAttrs objectEnumerator]; 1166 while ((attr = [enumerator nextObject])) { 1167 if ([[[attr columnName] lowercaseString] isEqual:pKey]) 1168 break; 1169 } 1170 NSAssert2(attr != nil, @"missing attribute for pkName %@ attrs %@", 1171 pKey, pkAttrs); 1172 } 1173 [result appendString:[_adaptor formatAttribute:attr]]; 1174 1175 1176 value = [(EOKeyGlobalID *)_gid keyValues][pkCnt++]; 1177 if (value == nil) value = null; 1178 1179 [result appendString:[value isNotNull] ? @"=" : @" IS "]; 1180 [result appendString:[_adaptor formatValue:value forAttribute:attr]]; 1181 } 1182 return result; 1183} 1184 1185- (NSDictionary *)_mapAttrsWithValues:(NSDictionary *)_keyValues 1186 tableName:(NSString *)_tableName 1187 channel:(EOAdaptorChannel *)_adChan 1188{ 1189 id *keys, *values; 1190 int mapCnt; 1191 NSEnumerator *en; 1192 EOAttribute *attr; 1193 NSDictionary *result; 1194 NSArray *attrs; 1195 1196 attrs = [_adChan attributesForTableName:_tableName]; 1197 mapCnt = [attrs count]; 1198 keys = calloc(mapCnt + 1, sizeof(id)); 1199 values = calloc(mapCnt + 1, sizeof(id)); 1200 en = [attrs objectEnumerator]; 1201 mapCnt = 0; 1202 1203 while ((attr = [en nextObject])) { 1204 id v; 1205 1206 v = (v = [_keyValues valueForKey:[[attr columnName] lowercaseString]]) 1207 ? v : (id)null; 1208 1209 keys[mapCnt] = attr; 1210 values[mapCnt] = v; 1211 mapCnt++; 1212 } 1213 result = [[NSDictionary alloc] 1214 initWithObjects:values forKeys:keys count:mapCnt]; 1215 free(keys); keys = NULL; 1216 free(values); values = NULL; 1217 return [result autorelease]; 1218} 1219 1220@end /* EOAdaptorDataSource(Private) */ 1221 1222@implementation EOAndQualifier(SqlExpression) 1223 1224- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 1225 attributes:(NSArray *)_attributes 1226{ 1227 NSMutableString *str = nil; 1228 NSEnumerator *enumerator = nil; 1229 EOQualifier *qual = nil; 1230 BOOL isFirst = YES; 1231 NSString *result = nil; 1232 1233 str = [[NSMutableString alloc] initWithCapacity:128]; 1234 1235 enumerator = [self->qualifiers objectEnumerator]; 1236 while ((qual = [enumerator nextObject])) { 1237 NSString *s; 1238 1239 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes]; 1240 if (isFirst) { 1241 [str appendFormat:@"(%@)", s]; 1242 isFirst = NO; 1243 } 1244 else 1245 [str appendFormat:@" AND (%@)", s]; 1246 } 1247 result = [str copy]; 1248 [str release]; str = nil; 1249 return [result autorelease]; 1250} 1251@end /* EOAndQualifier(SqlExpression) */ 1252 1253@implementation EOOrQualifier(SqlExpression) 1254 1255- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 1256 attributes:(NSArray *)_attributes 1257{ 1258 NSMutableString *str = nil; 1259 NSEnumerator *enumerator = nil; 1260 EOQualifier *qual = nil; 1261 BOOL isFirst = YES; 1262 NSString *result = nil; 1263 1264 str = [[NSMutableString alloc] initWithCapacity:128]; 1265 1266 enumerator = [self->qualifiers objectEnumerator]; 1267 while ((qual = [enumerator nextObject])) { 1268 NSString *s; 1269 1270 s = [qual sqlExpressionWithAdaptor:_adaptor attributes:_attributes]; 1271 if (isFirst) { 1272 [str appendFormat:@"(%@)", s]; 1273 isFirst = NO; 1274 } 1275 else 1276 [str appendFormat:@" OR (%@)", s]; 1277 } 1278 result = [str copy]; 1279 [str release]; str = nil; 1280 return [result autorelease]; 1281} 1282 1283@end /* EOOrQualifier(SqlExpression) */ 1284 1285@implementation EOKeyValueQualifier(SqlExpression) 1286 1287+ (NSString *)sqlStringForOperatorSelector:(SEL)_sel { 1288 static NSMapTable *selectorToOperator = NULL; 1289 NSString *s, *ss; 1290 1291 if ((s = NSStringFromSelector(_sel)) == nil) 1292 return nil; 1293 1294 if (selectorToOperator == NULL) { 1295 selectorToOperator = NSCreateMapTable(NSObjectMapKeyCallBacks, 1296 NSObjectMapValueCallBacks, 1297 10); 1298 NSMapInsert(selectorToOperator, 1299 NSStringFromSelector(EOQualifierOperatorEqual), 1300 @"="); 1301 NSMapInsert(selectorToOperator, 1302 NSStringFromSelector(EOQualifierOperatorNotEqual), 1303 @"<>"); 1304 NSMapInsert(selectorToOperator, 1305 NSStringFromSelector(EOQualifierOperatorLessThan), 1306 @"<"); 1307 NSMapInsert(selectorToOperator, 1308 NSStringFromSelector(EOQualifierOperatorGreaterThan), 1309 @">"); 1310 NSMapInsert(selectorToOperator, 1311 NSStringFromSelector(EOQualifierOperatorLessThanOrEqualTo), 1312 @"<="); 1313 NSMapInsert(selectorToOperator, 1314 NSStringFromSelector(EOQualifierOperatorGreaterThanOrEqualTo), 1315 @">="); 1316 } 1317 1318 if ((ss = NSMapGet(selectorToOperator, s))) 1319 return ss; 1320 1321 return nil; 1322} 1323 1324- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 1325 attributes:(NSArray *)_attributes 1326{ 1327 EOAttribute *attr = nil; 1328 NSEnumerator *en = nil; 1329 NSString *k = nil; 1330 NSString *sql = nil; 1331 NSString *sqlKey, *sqlValue; 1332 1333 k = [self->key lowercaseString]; 1334 en = [_attributes objectEnumerator]; 1335 1336 while ((attr = [en nextObject])) { 1337 if ([[[attr columnName] lowercaseString] isEqualToString:k]) { 1338 break; 1339 } 1340 } 1341 if (!attr) { 1342 en = [_attributes objectEnumerator]; 1343 while ((attr = [en nextObject])) { 1344 if ([[attr name] isEqualToString:self->key]) 1345 break; 1346 } 1347 } 1348 if (!attr) { 1349 NSLog(@"WARNING[%s]: missing attribute [%@] for qualifier %@", 1350 __PRETTY_FUNCTION__, 1351 _attributes, self); 1352 return @"1=2"; 1353 } 1354 1355 sqlKey = [_adaptor formatAttribute:attr]; 1356 1357 sqlValue = [_adaptor formatValue:(self->value ? self->value : (id)null) 1358 forAttribute:attr]; 1359 1360 sql = nil; 1361 1362 if (sel_isEqual(EOQualifierOperatorEqual, self->operator)) { 1363 if ([self->value isNotNull]) 1364 sql = [NSString stringWithFormat:@"%@ = %@", sqlKey, sqlValue]; 1365 else 1366 sql = [NSString stringWithFormat:@"%@ IS NULL", sqlKey]; 1367 } 1368 else if (sel_isEqual(EOQualifierOperatorNotEqual, self->operator)) { 1369 if ([self->value isNotNull]) 1370 sql = [NSString stringWithFormat:@"NOT (%@ = %@)", sqlKey, sqlValue]; 1371 else 1372 sql = [NSString stringWithFormat:@"%@ IS NOT NULL", sqlKey]; 1373 } 1374 else if (sel_isEqual(EOQualifierOperatorLessThan, self->operator)) { 1375 sql = [NSString stringWithFormat:@"%@ < %@", sqlKey, sqlValue]; 1376 } 1377 else if (sel_isEqual(EOQualifierOperatorLessThanOrEqualTo, self->operator)) { 1378 sql = [NSString stringWithFormat:@"%@ <= %@", sqlKey, sqlValue]; 1379 } 1380 else if (sel_isEqual(EOQualifierOperatorGreaterThan, self->operator)) { 1381 sql = [NSString stringWithFormat:@"%@ > %@", sqlKey, sqlValue]; 1382 } 1383 else if (sel_isEqual(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) { 1384 sql = [NSString stringWithFormat:@"%@ >= %@", sqlKey, sqlValue]; 1385 } 1386 else if (sel_isEqual(EOQualifierOperatorLike, self->operator)) { 1387 sqlValue = [[self->value stringValue] 1388 stringByReplacingString:@"*" withString:@"%"]; 1389 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr]; 1390 1391 sql = [NSString stringWithFormat:@"%@ LIKE %@", sqlKey, sqlValue]; 1392 } 1393 else if (sel_isEqual(EOQualifierOperatorCaseInsensitiveLike, self->operator)) { 1394 sqlValue = [[self->value stringValue] 1395 stringByReplacingString:@"*" withString:@"%"]; 1396 sqlValue = [sqlValue lowercaseString]; 1397 sqlValue = [_adaptor formatValue:sqlValue forAttribute:attr]; 1398 1399 sql = [NSString stringWithFormat:@"LOWER(%@) LIKE %@", sqlKey, sqlValue]; 1400 } 1401#if 0 1402 else if (sel_isEqual(EOQualifierOperatorLessThanOrEqualTo, self->operator)) { 1403 } 1404 else if (sel_isEqual(EOQualifierOperatorGreaterThanOrEqualTo, self->operator)) { 1405 } 1406#endif 1407 else { 1408 NSLog(@"ERROR(%s): unsupported SQL operator: %@", __PRETTY_FUNCTION__, 1409 [EOQualifier stringForOperatorSelector:self->operator]); 1410 [self notImplemented:_cmd]; 1411 return nil; 1412 } 1413 1414 return sql; 1415} 1416 1417@end /* EOKeyValueQualifier(SqlExpression) */ 1418 1419@implementation EONotQualifier(SqlExpression) 1420 1421- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 1422 attributes:(NSArray *)_attributes 1423{ 1424 NSString *s; 1425 1426 s = [self->qualifier sqlExpressionWithAdaptor:_adaptor 1427 attributes:_attributes]; 1428 return [NSString stringWithFormat:@"NOT(%@)", s]; 1429} 1430 1431@end /* EONotQualifier(SqlExpression) */ 1432 1433@implementation EOKeyComparisonQualifier(SqlExpression) 1434 1435- (NSString *)sqlExpressionWithAdaptor:(EOAdaptor *)_adaptor 1436 attributes:(NSArray *)_attributes 1437{ 1438 NSLog(@"ERROR(%s): subclass needs to override this method!", 1439 __PRETTY_FUNCTION__); 1440 return nil; 1441} 1442 1443@end /* EOKeyComparisonQualifier(SqlExpression) */ 1444