4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
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.
27 #include "EOSQLExpression.h"
30 #import "EOAdaptorChannel.h"
31 #import "EOAdaptorContext.h"
32 #import "EOAttribute.h"
33 #import "EOAttributeOrdering.h"
35 #import "EOFExceptions.h"
36 #import "EOSQLQualifier.h"
37 #import "EORelationship.h"
38 #import <EOControl/EOFetchSpecification.h>
39 #import <EOControl/EONull.h>
40 #import <EOControl/EOQualifier.h>
41 #import <EOControl/EOSortOrdering.h>
43 #if LIB_FOUNDATION_LIBRARY
44 # include <extensions/DefaultScannerHandler.h>
45 # include <extensions/PrintfFormatScanner.h>
47 # include "DefaultScannerHandler.h"
48 # include "PrintfFormatScanner.h"
52 A SQL command is generated for an entity when you fetch, insert, update or
53 delete an object from the database. The command includes all the attributes
56 The biggest problem in generation of SQL commands is the generation of SELECT
57 statements, because the tables have to be properly aliased. If an entity has
58 only simple attributes then you have a single table in the FROM clause. If the
59 entity has flattened attributes, then several tables appear in the FROM list;
60 each one with a different alias.
62 The algorithm uses a dictionary that has as keys either entities or
63 relationships. The values in dictionary are aliases.
65 For a simple attribute we insert in the above dictionary the attribute's entity
66 as key and an alias for it.
68 For a flattened attribute with the following definition: `toEntity1.toEntity2.
69 ... toEntityn.attribute', we have to assign to each toEntityi object an alias.
71 Let's see how each component of the SELECT command is generated. The columns
72 list is generated by iterating over the attributes of entity. If the attribute
73 is simple, its entity is looked up in the aliases dictionary. If the attribute
74 is flattened, the alias of attribute is the alias of toEntityn. If the
75 attribute is derived, each component of it is generated applying the same
78 The FROM list is generated like this. For each key in the aliases dictionary:
79 * if the key is an entity, its external name is written followed by the alias
80 corresponding to entity
81 * if the key is a relationship, the external name of its destination entity is
82 written followed by the alias corresponding to relationship.
84 A little bit complicated is the WHERE clause. For each flattened attribute we
85 have to generate the logic expression that selects the attribute by expressing
86 the joins over several entities. The algorithm starts with the first
87 relationship. An additional variable, named context is used. Its initial value
88 is the current entity. The expression
90 context-alias.source-attribute join-operator
91 relationship-alias.destination-attribute
93 is added using the AND operator to the where clause. Then the context variable
94 is set to the current relationship. The algorithm continues with the next
95 relationship until there are no more relationships to process.
99 static EONull *null = nil;
100 NSString *EOBindVariableNameKey = @"name";
101 NSString *EOBindVariablePlaceHolderKey = @"placeHolder";
102 NSString *EOBindVariableAttributeKey = @"attribute";
103 NSString *EOBindVariableValueKey = @"value";
106 @interface EOInsertUpdateScannerHandler : DefaultScannerHandler
109 EOAttribute *attribute;
113 - (void)setValue:(NSString*)value
114 attribute:(EOAttribute*)attribute
115 adaptor:(EOAdaptor*)adaptor;
119 @implementation EOInsertUpdateScannerHandler
125 = [self methodForSelector:@selector(convertValue:scanner:)];
130 RELEASE(self->value);
131 RELEASE(self->attribute);
132 RELEASE(self->adaptor);
136 - (void)setValue:(NSString *)_value
137 attribute:(EOAttribute*)_attribute
138 adaptor:(EOAdaptor*)_adaptor
140 ASSIGNCOPY(self->value, _value);
141 ASSIGN(self->attribute, _attribute);
142 ASSIGN(self->adaptor, _adaptor);
145 - (NSString *)convertValue:(va_list *)pString scanner:(FormatScanner *)scanner{
146 return (self->adaptor)
147 ? [self->adaptor formatValue:self->value?self->value:(id)null
148 forAttribute:attribute]
152 @end /* EOInsertUpdateScannerHandler */
155 @implementation EOSQLExpression
162 if (null == nil) null = [[NSNull null] retain];
165 - (id)initWithEntity:(EOEntity *)_entity {
166 if ((self = [super init])) {
167 ASSIGN(self->entity, _entity);
172 return [self initWithEntity:nil];
175 + (id)deleteExpressionWithQualifier:(EOSQLQualifier*)qualifier
176 channel:(EOAdaptorChannel*)channel
178 EOSQLExpression* sqlExpression
179 = AUTORELEASE([[self deleteExpressionClass] new]);
180 [sqlExpression deleteExpressionWithQualifier:qualifier channel:channel];
181 return sqlExpression;
183 + (id)insertExpressionForRow:(NSDictionary *)row
184 entity:(EOEntity*)_entity
185 channel:(EOAdaptorChannel*)channel
187 EOSQLExpression* sqlExpression
188 = AUTORELEASE([[self insertExpressionClass] new]);
190 [sqlExpression insertExpressionForRow:row entity:_entity channel:channel];
191 return sqlExpression;
194 - (id)deleteExpressionWithQualifier:(EOSQLQualifier *)qualifier
195 channel:(EOAdaptorChannel*)channel
199 self = [self initWithEntity:[qualifier entity]];
201 self->adaptor = RETAIN([[channel adaptorContext] adaptor]);
203 sql = [self assembleDeleteStatementWithQualifier:qualifier
204 tableList:[entity externalName]
205 whereClause:[self whereClauseForQualifier:qualifier]];
207 self->content = [sql mutableCopy];
208 [self finishBuildingExpression];
213 - (id)insertExpressionForRow:(NSDictionary *)row
214 entity:(EOEntity*)_entity
215 channel:(EOAdaptorChannel*)channel
219 self = [self initWithEntity:_entity];
221 self->adaptor = RETAIN([[channel adaptorContext] adaptor]);
223 sql = [self assembleInsertStatementWithRow:row
224 tableList:[entity externalName]
225 columnList:[self columnListForRow:row]
226 valueList:[self valueListForRow:row]];
228 self->content = [sql mutableCopy];
230 [self finishBuildingExpression];
235 + (id)selectExpressionForAttributes:(NSArray *)attributes
237 qualifier:(EOSQLQualifier *)qualifier
238 fetchOrder:(NSArray *)fetchOrder
239 channel:(EOAdaptorChannel *)channel
241 EOSQLExpression* sqlExpression
242 = AUTORELEASE([[self selectExpressionClass] new]);
243 [sqlExpression selectExpressionForAttributes:attributes
246 fetchOrder:fetchOrder
248 return sqlExpression;
251 - (id)selectExpressionForAttributes:(NSArray *)attributes
253 qualifier:(EOSQLQualifier *)qualifier
254 fetchOrder:(NSArray *)fetchOrder
255 channel:(EOAdaptorChannel *)channel
257 NSArray *relationshipPaths;
260 self = [self initWithEntity:[qualifier entity]];
262 self->adaptor = [[[channel adaptorContext] adaptor] retain];
263 // self->content = [NSMutableString new]; // mh: BUG!!!
265 relationshipPaths = [self relationshipPathsForAttributes:attributes
267 fetchOrder:fetchOrder];
269 sql = [self assembleSelectStatementWithAttributes:attributes
272 fetchOrder:fetchOrder
274 [qualifier usesDistinct] ? @"SELECT DISTINCT" : @"SELECT"
276 [self selectListWithAttributes:attributes
278 tableList:[self fromClause]
279 whereClause:[self whereClauseForQualifier:qualifier]
281 [self joinExpressionForRelationshipPaths:relationshipPaths]
282 orderByClause:[self orderByClauseForFetchOrder:fetchOrder]
283 lockClause:flag ? [self lockClause] : nil];
285 self->content = [sql mutableCopy];
287 [self finishBuildingExpression];
291 + (id)updateExpressionForRow:(NSDictionary *)row
292 qualifier:(EOSQLQualifier *)qualifier
293 channel:(EOAdaptorChannel *)channel
295 EOSQLExpression* sqlExpression
296 = AUTORELEASE([[self updateExpressionClass] new]);
297 [sqlExpression updateExpressionForRow:row
300 return sqlExpression;
303 - (id)updateExpressionForRow:(NSDictionary *)row
304 qualifier:(EOSQLQualifier *)qualifier
305 channel:(EOAdaptorChannel *)channel
309 self = [self initWithEntity:[qualifier entity]];
310 if (self->entity == nil) {
311 [[[InvalidQualifierException alloc]
312 initWithFormat:@"entity for qualifier %@ is nil",
313 [qualifier expressionValueForContext:nil]] raise];
316 self->adaptor = [[[channel adaptorContext] adaptor] retain];
317 self->content = [[NSMutableString alloc] init];
319 sql = [self assembleUpdateStatementWithRow:row
321 tableList:[entity externalName]
322 updateList:[self updateListForRow:row]
323 whereClause:[self whereClauseForQualifier:qualifier]];
324 [self->content appendString:sql];
326 [self finishBuildingExpression];
332 RELEASE(self->bindings);
333 RELEASE(self->listString);
334 RELEASE(self->whereClauseString);
335 RELEASE(self->entity);
336 RELEASE(self->adaptor);
337 RELEASE(self->entitiesAndPropertiesAliases);
338 RELEASE(self->fromListEntities);
339 RELEASE(self->content);
343 - (NSString *)selectListWithAttributes:(NSArray *)attributes
344 qualifier:(EOSQLQualifier *)qualifier
346 NSMutableString *selectList;
347 NSEnumerator *enumerator;
349 EOAttribute *attribute;
352 selectList = [NSMutableString stringWithCapacity:128];
353 isDistinct = [qualifier usesDistinct];
355 /* check whether DISTINCT is allowed */
356 enumerator = [attributes objectEnumerator];
357 while ((attribute = [enumerator nextObject])) {
359 if (![self->adaptor attributeAllowedInDistinctSelects:attribute]) {
361 NSLog(@"WARNING: tried to select attribute type %@ with DISTINCT "
362 @"which is not allowed, disabling DISTINCT: %@",
363 [attribute externalType], [attribute name]);
371 enumerator = [attributes objectEnumerator];
372 while ((attribute = [enumerator nextObject])) {
376 [selectList appendString:@", "];
378 [selectList appendString:[self expressionValueForAttribute:attribute]];
384 - (NSString *)fromClause {
385 NSMutableString *fromClause;
386 NSEnumerator *enumerator;
390 fromClause = [NSMutableString stringWithCapacity:64];
391 enumerator = [self->fromListEntities objectEnumerator];
393 /* Compute the FROM list from all the aliases found in
394 entitiesAndPropertiesAliases dictionary. Note that this dictionary
395 contains entities and relationships. The last ones are there for
396 flattened attributes over reflexive relationships. */
397 while ((key = [enumerator nextObject])) {
401 [fromClause appendString:@", "];
403 if ([key isKindOfClass:[EORelationship class]]) {
404 /* This key corresponds to a flattened attribute. */
405 [fromClause appendFormat:@"%@ %@",
406 [[key destinationEntity] externalName],
407 [entitiesAndPropertiesAliases objectForKey:key]];
410 /* This is an EOEntity. */
411 [fromClause appendFormat:@"%@ %@",
413 [entitiesAndPropertiesAliases objectForKey:key]];
420 - (NSString *)whereClauseForQualifier:(EOSQLQualifier *)_qualifier {
421 return [_qualifier expressionValueForContext:self];
424 - (id)joinExpressionForRelationshipPaths:(NSArray *)relationshipPaths
426 NSMutableString *expression = [NSMutableString stringWithCapacity:64];
427 NSEnumerator *enumerator = [relationshipPaths objectEnumerator];
428 NSMutableArray *rels = [NSMutableArray new];
429 NSArray *relationshipPath;
430 EORelationship *relationship;
433 while ((relationshipPath = [enumerator nextObject])) {
434 NSEnumerator *componentRelationshipsEnumerator;
437 componentRelationshipsEnumerator = [relationshipPath objectEnumerator];
440 while((relationship = [componentRelationshipsEnumerator nextObject])) {
441 if (![rels containsObject:relationship]) {
442 id sourceAttribute, destinationAttribute;
444 [rels addObject:relationship];
446 /* Compute the SQL expression string corresponding to each join in
448 sourceAttribute = [self expressionValueForAttribute:
449 [relationship sourceAttribute]
451 destinationAttribute = [self expressionValueForAttribute:
452 [relationship destinationAttribute]
454 [relationship destinationEntity]];
456 //NSLog(@"sourceAttribute %@", sourceAttribute);
457 //NSLog(@"destinationAttribute %@", destinationAttribute);
462 [expression appendString:@" AND "];
464 [expression appendString:[sourceAttribute description]];
465 [expression appendString:@" = "];
466 [expression appendString:[destinationAttribute description]];
468 /* Compute the next context which is the current relationship. */
469 context = [relationship destinationEntity];
476 - (NSString *)orderByClauseForFetchOrder:(NSArray *)fetchOrder
479 NSMutableString *orderBy;
481 if ((count = [fetchOrder count]) == 0)
484 orderBy = (id)[NSMutableString stringWithCapacity:32];
485 for(i = 0; i < count; i++) {
488 eorder = [fetchOrder objectAtIndex:i];
490 if (i != 0) [orderBy appendString:@", "];
492 if ([eorder isKindOfClass:[EOSortOrdering class]]) {
493 EOSortOrdering *order;
494 EOAttribute *attribute;
499 ordering = [order selector];
500 attribute = [self->entity attributeNamed:[order key]];
502 if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
503 sel_eq(ordering, EOCompareCaseInsensitiveDescending))
508 [orderBy appendFormat:fmt, [self expressionValueForAttribute:attribute]];
510 if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
511 sel_eq(ordering, EOCompareAscending)) {
512 [orderBy appendString:@" ASC"];
514 else if (sel_eq(ordering, EOCompareCaseInsensitiveDescending) ||
515 sel_eq(ordering, EOCompareDescending)) {
516 [orderBy appendString:@" DESC"];
520 EOAttributeOrdering *order;
524 ordering = [order ordering];
526 [orderBy appendFormat:@"%@",
527 [self expressionValueForAttribute:[order attribute]]];
528 if (ordering != EOAnyOrder)
529 [orderBy appendString:
530 ([order ordering] == EOAscendingOrder ? @" ASC" : @" DESC")];
537 - (NSString *)literalForAttribute:(EOAttribute *)_attribute
538 withValue:(id)_value fromRow:(NSDictionary *)_row
540 return (self->adaptor)
541 ? [self->adaptor formatValue:_value?_value:null forAttribute:_attribute]
542 : [_value stringValue];
545 - (id)updateListForRow:(NSDictionary *)row {
546 PrintfFormatScanner *formatScanner = nil;
547 EOInsertUpdateScannerHandler *scannerHandler = nil;
548 NSMutableString *expression = nil;
549 NSEnumerator *enumerator;
550 NSString *attributeName = nil;
553 enumerator = [row keyEnumerator];
554 expression = [NSMutableString stringWithCapacity:256];
556 formatScanner = [[PrintfFormatScanner alloc] init];
557 AUTORELEASE(formatScanner);
558 scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
559 AUTORELEASE(scannerHandler);
561 [formatScanner setAllowOnlySpecifier:YES];
562 [formatScanner setFormatScannerHandler:scannerHandler];
564 while((attributeName = [enumerator nextObject])) {
565 EOAttribute *attribute;
566 NSString *updateFormat;
567 NSString *columnName = nil;
570 attribute = [entity attributeNamed:attributeName];
571 updateFormat = [attribute updateFormat];
573 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
575 ? [adaptor formatAttribute:attribute]
576 : [attribute columnName];
578 value = [row objectForKey:attributeName];
581 [scannerHandler setValue:value
584 #if defined(__s390__)
585 value = [formatScanner performSelector:
586 @selector(stringWithFormat:arguments:)
587 withObject:updateFormat
590 value = [formatScanner stringWithFormat:updateFormat
595 value = [self literalForAttribute:attribute
596 withValue:value fromRow:row];
599 if(first) first = NO;
600 else [expression appendString:@", "];
602 [expression appendString:columnName];
603 [expression appendString:@" = "];
604 [expression appendString:value];
610 - (id)columnListForRow:(NSDictionary *)row {
611 NSMutableString *expression;
612 NSEnumerator *enumerator;
613 NSString *attributeName = nil;
616 expression = [NSMutableString stringWithCapacity:128];
617 enumerator = [row keyEnumerator];
619 while ((attributeName = [enumerator nextObject])) {
620 EOAttribute *attribute;
621 NSString *columnName;
623 attribute = [entity attributeNamed:attributeName];
625 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
627 ? [adaptor formatAttribute:attribute]
628 : [attribute columnName];
630 if (first) first = NO;
631 else [expression appendString:@", "];
633 [expression appendString:columnName];
639 - (id)valueListForRow:(NSDictionary *)row {
640 EOInsertUpdateScannerHandler *scannerHandler;
641 PrintfFormatScanner *formatScanner;
642 NSEnumerator *enumerator;
643 NSString *attributeName = nil;
644 NSMutableString *expression = nil;
647 formatScanner = [[PrintfFormatScanner alloc] init];
648 AUTORELEASE(formatScanner);
649 scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
650 AUTORELEASE(scannerHandler);
652 expression = [NSMutableString stringWithCapacity:256];
653 enumerator = [row keyEnumerator];
655 [formatScanner setAllowOnlySpecifier:YES];
656 [formatScanner setFormatScannerHandler:scannerHandler];
658 while ((attributeName = [enumerator nextObject])) {
659 EOAttribute *attribute;
660 NSString *insertFormat;
663 attribute = [entity attributeNamed:attributeName];
664 insertFormat = [attribute insertFormat];
665 value = [row objectForKey:attributeName];
667 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
669 [scannerHandler setValue:value
671 adaptor:self->adaptor];
672 #if defined(__s390__)
673 value = [formatScanner performSelector:
674 @selector(stringWithFormat:arguments:)
675 withObject:insertFormat
678 value = [formatScanner stringWithFormat:insertFormat
683 value = [self literalForAttribute:attribute
684 withValue:value fromRow:row];
687 if(first) first = NO;
688 else [expression appendString:@", "];
690 [expression appendString:value];
696 - (NSArray *)relationshipPathsForAttributes:(NSArray *)attributes
697 qualifier:(EOSQLQualifier *)qualifier
698 fetchOrder:(NSArray *)fetchOrder
701 NSMutableSet *entities;
702 NSMutableSet *relationshipPaths;
703 NSEnumerator *enumerator;
704 id entityOrRelationship;
706 entities = [NSMutableSet set];
707 relationshipPaths = [NSMutableSet set];
709 NSAssert3(self->entity,
710 @"entity should be non nil (attrs=%@, qual=%@, order=%@)",
711 attributes, qualifier, fetchOrder);
713 for (i = 0, count = [attributes count]; i < count; i++) {
714 EOAttribute *attribute;
716 attribute = [attributes objectAtIndex:i];
718 if ([attribute entity] != entity) {
719 [[[InvalidAttributeException alloc]
720 initWithFormat:@"all attributes must be from the same "
721 @"entity (attribute '%@' is not in '%@')",
723 [entity name]] raise];
725 if ([attribute isFlattened]) {
726 id definitionArray = [attribute definitionArray];
727 NSRange range = { 0, [definitionArray count] - 1 };
728 id relationshipPath = [definitionArray subarrayWithRange:range];
730 [relationshipPaths addObject:relationshipPath];
731 [entities addObjectsFromArray:relationshipPath];
734 /* attribute is normal. */
735 [entities addObject:[attribute entity]];
739 [relationshipPaths unionSet:[qualifier relationshipPaths]];
740 [entities unionSet:[qualifier additionalEntities]];
742 for (i = 0, count = [fetchOrder count]; i < count; i++) {
743 EOAttribute *attribute;
746 eorder = [fetchOrder objectAtIndex:i];
748 attribute = ([eorder isKindOfClass:[EOSortOrdering class]])
749 ? [self->entity attributeNamed:[(EOSortOrdering *)eorder key]]
750 : [(EOAttributeOrdering *)eorder attribute];
752 if ([attribute entity] != entity) {
753 [[[InvalidAttributeException alloc]
754 initWithFormat:@"all attributes must be from the same "
755 @"entity (attribute '%@' is not in '%@')",
757 [entity name]] raise];
759 if ([attribute isFlattened]) {
760 id definitionArray = [attribute definitionArray];
761 NSRange range = { 0, [definitionArray count] - 1 };
762 id relationshipPath = [definitionArray subarrayWithRange:range];
764 [relationshipPaths addObject:relationshipPath];
765 [entities addObjectsFromArray:relationshipPath];
769 entitiesAndPropertiesAliases = [NSMutableDictionary new];
770 fromListEntities = [NSMutableArray new];
771 enumerator = [entities objectEnumerator];
773 while ((entityOrRelationship = [enumerator nextObject])) {
774 NSString* alias = [NSString stringWithFormat:@"t%d", i++];
776 [entitiesAndPropertiesAliases setObject:alias
777 forKey:entityOrRelationship];
778 [fromListEntities addObject:entityOrRelationship];
781 return [relationshipPaths allObjects];
784 - (EOEntity *)entity {
787 - (id)finishBuildingExpression {
790 - (NSString *)lockClause {
794 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute
797 NSString *alias = [entitiesAndPropertiesAliases objectForKey:context];
798 NSString *columnName = nil;
801 ? [adaptor formatAttribute:attribute]
802 : [attribute columnName];
805 ? [NSString stringWithFormat:@"%@.%@", alias, columnName]
809 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute {
810 if([attribute isFlattened])
811 return [self expressionValueForAttributePath:
812 [attribute definitionArray]];
813 else if([attribute isDerived])
814 return [[attribute definitionArray] expressionValueForContext:self];
816 /* attribute is a normal attribute. Its alias is the alias
818 return [self expressionValueForAttribute:attribute
819 context:[attribute entity]];
822 - (NSString *)expressionValueForAttributePath:(NSArray *)definitionArray {
823 /* Take the alias of the last relationship. */
828 = [definitionArray objectAtIndex:([definitionArray count] - 2)];
829 alias = [entitiesAndPropertiesAliases objectForKey:relationship];
831 return AUTORELEASE(([[NSString alloc]
832 initWithFormat:@"%@.%@",
833 alias, [[definitionArray lastObject] columnName]]));
836 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)context {
837 return self->content;
840 + (Class)selectExpressionClass {
841 return [EOSelectSQLExpression class];
843 + (Class)insertExpressionClass {
844 return [EOInsertSQLExpression class];
846 + (Class)deleteExpressionClass {
847 return [EODeleteSQLExpression class];
849 + (Class)updateExpressionClass {
850 return [EOUpdateSQLExpression class];
853 - (EOAdaptor *)adaptor {
854 return self->adaptor;
859 - (NSString *)description {
862 ms = [NSMutableString stringWithCapacity:128];
863 [ms appendFormat:@"<0x%08X[%@]:\n", self, NSStringFromClass([self class])];
865 if (self->entity) [ms appendFormat:@" entity=%@\n", self->entity];
866 if (self->adaptor) [ms appendFormat:@" adaptor=%@\n", self->adaptor];
867 if (self->content) [ms appendFormat:@" content='%@\n'", self->content];
869 if (self->entitiesAndPropertiesAliases)
870 [ms appendFormat:@" aliases=%@\n", self->entitiesAndPropertiesAliases];
871 if (self->fromListEntities)
872 [ms appendFormat:@" from-entities=%@\n", self->fromListEntities];
874 if (self->whereClauseString)
875 [ms appendFormat:@" where=%@\n", self->whereClauseString];
877 if (self->listString) [ms appendFormat:@" list=%@\n", self->listString];
878 if (self->bindings) [ms appendFormat:@" bindings=%@\n", self->bindings];
880 [ms appendString:@">"];
884 @end /* EOSQLExpression */
887 @implementation EOInsertSQLExpression
888 @end /* EOInsertSQLExpression */
890 @implementation EOUpdateSQLExpression
891 @end /* EOUpdateSQLExpression */
893 @implementation EODeleteSQLExpression
894 @end /* EODeleteSQLExpression */
896 @implementation EOSQLExpression(NewInEOF2)
898 + (EOSQLExpression *)selectStatementForAttributes:(NSArray *)_attributes
900 fetchSpecification:(EOFetchSpecification *)_fspec
901 entity:(EOEntity *)_entity
904 [NSException raise:NSInvalidArgumentException
905 format:@"missing fetch specification argument .."];
907 if ([_attributes count] == 0) {
908 [NSException raise:NSInvalidArgumentException
909 format:@"missing attributes for select .."];
912 return [self selectExpressionForAttributes:_attributes
914 qualifier:[[_fspec qualifier] sqlQualifierForEntity:_entity]
915 fetchOrder:[_fspec sortOrderings]
919 + (EOSQLExpression *)expressionForString:(NSString *)_sql {
922 se = [[EOSQLExpression alloc] init];
923 [se setStatement:_sql];
924 return AUTORELEASE(se);
929 - (void)setStatement:(NSString *)_stmt {
932 self->content = [_stmt mutableCopy];
935 - (NSString *)statement {
936 return self->content;
939 - (NSString *)whereClauseString {
940 return self->whereClauseString;
945 - (NSString *)tableListWithRootEntity:(EOEntity *)_entity {
946 return ([self->fromListEntities count] > 0)
948 : [_entity externalName];
953 - (NSString *)assembleDeleteStatementWithQualifier:(EOQualifier *)_qualifier
954 tableList:(NSString *)_tableList
955 whereClause:(NSString *)_whereClause
959 s = [NSMutableString stringWithCapacity:64];
960 [s appendString:@"DELETE FROM "];
961 [s appendString:_tableList];
962 [s appendString:@" WHERE "];
963 [s appendString:_whereClause];
967 - (NSString *)assembleInsertStatementWithRow:(NSDictionary *)_row
968 tableList:(NSString *)_tables
969 columnList:(NSString *)_columns
970 valueList:(NSString *)_values
974 s = [NSMutableString stringWithCapacity:256];
975 [s appendString:@"INSERT INTO "];
976 [s appendString:_tables];
977 [s appendString:@" ("];
978 [s appendString:_columns];
979 [s appendString:@") VALUES ("];
980 [s appendString:_values];
981 [s appendString:@")"];
985 - (NSString *)assembleSelectStatementWithAttributes:(NSArray *)_attributes
987 qualifier:(EOQualifier *)_qualifier
988 fetchOrder:(NSArray *)_fetchOrder
989 selectString:(NSString *)_selectString
990 columnList:(NSString *)_columns
991 tableList:(NSString *)_tables
992 whereClause:(NSString *)_whereClause
993 joinClause:(NSString *)_joinClause
994 orderByClause:(NSString *)_orderByClause
995 lockClause:(NSString *)_lockClause
1001 #warning DEBUG LOG, REMOVE!
1002 [self logWithFormat:@"%s: '%@': %@", __PRETTY_FUNCTION__, _whereClause,self];
1005 s = [NSMutableString stringWithCapacity:256];
1007 [s appendString:_selectString ? _selectString : @"SELECT"];
1008 [s appendString:@" "];
1009 [s appendString:_columns];
1010 [s appendString:@" FROM "];
1011 [s appendString:_tables];
1013 if ([_lockClause length] > 0) {
1014 [s appendString:@" "];
1015 [s appendString:_lockClause];
1018 wlen = [_whereClause length];
1019 jlen = [_joinClause length];
1021 if ((wlen > 0) || (jlen > 0))
1022 [s appendString:@" WHERE "];
1025 [s appendString:_whereClause];
1027 if ((wlen > 0) && (jlen > 0))
1028 [s appendString:@" AND "];
1031 [s appendString:_joinClause];
1033 if ([_orderByClause length] > 0) {
1034 [s appendString:@" ORDER BY "];
1035 [s appendString:_orderByClause];
1041 - (NSString *)assembleUpdateStatementWithRow:(NSDictionary *)_row
1042 qualifier:(EOQualifier *)_qualifier
1043 tableList:(NSString *)_tables
1044 updateList:(NSString *)_updates
1045 whereClause:(NSString *)_whereClause
1049 s = [NSMutableString stringWithCapacity:256];
1051 [s appendString:@"UPDATE "];
1052 [s appendString:_tables];
1053 [s appendString:@" SET "];
1054 [s appendString:_updates];
1055 [s appendString:@" WHERE "];
1056 [s appendString:_whereClause];
1061 - (NSString *)assembleJoinClauseWithLeftName:(NSString *)_leftName
1062 rightName:(NSString *)_rightName
1063 joinSemantic:(EOJoinSemantic)_semantic
1067 s = [NSMutableString stringWithCapacity:64];
1068 [s appendString:_leftName];
1069 switch (_semantic) {
1071 [s appendString:@" = "];
1073 case EOFullOuterJoin:
1074 [s appendString:@" *=* "];
1076 case EOLeftOuterJoin:
1077 [s appendString:@" *= "];
1079 case EORightOuterJoin:
1080 [s appendString:@" =* "];
1083 [s appendString:_rightName];
1089 - (NSString *)sqlStringForAttribute:(EOAttribute *)_attribute {
1090 NSLog(@"ERROR(%s): subclasses need to override this method!",
1091 __PRETTY_FUNCTION__);
1095 - (NSString *)sqlStringForAttributePath:(NSString *)_attrPath {
1096 NSLog(@"ERROR(%s): subclasses need to override this method!",
1097 __PRETTY_FUNCTION__);
1101 - (NSString *)sqlStringForAttributeNamed:(NSString *)_attrName {
1104 if ((a = [[self entity] attributeNamed:_attrName]))
1105 return [self sqlStringForAttribute:a];
1107 return [self sqlStringForAttributePath:_attrName];
1110 /* bind variables */
1112 + (BOOL)useBindVariables {
1115 - (BOOL)mustUseBindVariableForAttribute:(EOAttribute *)_attr {
1118 - (BOOL)shouldUseBindVariableForAttribute:(EOAttribute *)_attr {
1122 - (NSMutableDictionary *)bindVariableDictionaryForAttribute:(EOAttribute *)_attr
1125 NSMutableDictionary *d;
1127 d = [NSMutableDictionary dictionaryWithCapacity:8];
1128 [d setObject:_attr forKey:EOBindVariableAttributeKey];
1129 [d setObject:_value ? _value : null forKey:EOBindVariableValueKey];
1133 - (void)addBindVariableDictionary:(NSMutableDictionary *)_dictionary {
1134 if (self->bindings == nil)
1135 self->bindings = [[NSMutableArray alloc] init];
1137 - (NSArray *)bindVariableDictionaries {
1138 return self->bindings;
1143 + (NSString *)formatValue:(id)_value forAttribute:(EOAttribute *)_attribute {
1144 return _value ? _value : null;
1147 - (NSString *)sqlStringForValue:(id)_value attributeNamed:(NSString *)_attrName {
1148 NSMutableDictionary *bindVars;
1149 EOAttribute *attribute;
1151 attribute = [[self entity] attributeNamed:_attrName];
1153 if ([self mustUseBindVariableForAttribute:attribute])
1154 bindVars = [self bindVariableDictionaryForAttribute:attribute value:_value];
1155 else if ([[self class] useBindVariables] &&
1156 [self shouldUseBindVariableForAttribute:attribute]) {
1157 bindVars = [self bindVariableDictionaryForAttribute:attribute value:_value];
1163 [self addBindVariableDictionary:bindVars];
1164 return [bindVars objectForKey:EOBindVariablePlaceHolderKey];
1167 return [[self class] formatValue:_value?_value:null forAttribute:attribute];
1170 + (NSString *)sqlPatternFromShellPattern:(NSString *)_pattern {
1173 if ((len = [_pattern length]) > 0) {
1174 unsigned cstrLen = [_pattern cStringLength];
1175 char cstrBuf[cstrLen + 1];
1177 char buf[len * 3 + 1];
1179 BOOL didSomething = NO;
1181 [_pattern getCString:cstrBuf];
1184 for (i = 0; *cstr; cstr++) {
1187 buf[i] = '%'; i++; didSomething = YES;
1190 buf[i] = '_'; i++; didSomething = YES;
1215 return (didSomething)
1216 ? [NSString stringWithCString:buf length:i]
1224 + (NSString *)formatSQLString:(NSString *)_sqlString format:(NSString *)_fmt {
1228 /* qualifier operators */
1230 - (NSString *)sqlStringForSelector:(SEL)_selector value:(id)_value {
1231 if ((_value == null) || (_value == nil)) {
1232 if (sel_eq(_selector, EOQualifierOperatorEqual))
1234 else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1238 if (sel_eq(_selector, EOQualifierOperatorEqual))
1240 else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1244 if (sel_eq(_selector, EOQualifierOperatorLessThan))
1246 else if (sel_eq(_selector, EOQualifierOperatorGreaterThan))
1248 else if (sel_eq(_selector, EOQualifierOperatorLessThanOrEqualTo))
1250 else if (sel_eq(_selector, EOQualifierOperatorGreaterThanOrEqualTo))
1252 else if (sel_eq(_selector, EOQualifierOperatorLike))
1255 return [NSString stringWithFormat:@"UNKNOWN<%@>",
1256 NSStringFromSelector(_selector)];
1262 - (NSString *)sqlStringForKeyComparisonQualifier:(EOKeyComparisonQualifier *)_q {
1267 s = [NSMutableString stringWithCapacity:64];
1269 sql = [self sqlStringForAttributeNamed:[_q leftKey]];
1270 a = [[self entity] attributeNamed:[_q leftKey]]; /* relationships ? */
1271 sql = [[self class] formatSQLString:sql format:[a readFormat]];
1272 [s appendString:sql];
1274 [s appendString:@" "];
1275 [s appendString:[self sqlStringForSelector:[_q selector] value:nil]];
1276 [s appendString:@" "];
1278 sql = [self sqlStringForAttributeNamed:[_q rightKey]];
1279 a = [[self entity] attributeNamed:[_q rightKey]]; /* relationships ? */
1280 sql = [[self class] formatSQLString:sql format:[a readFormat]];
1281 [s appendString:sql];
1286 - (NSString *)sqlStringForKeyValueQualifier:(EOKeyValueQualifier *)_q {
1293 s = [NSMutableString stringWithCapacity:64];
1295 sql = [self sqlStringForAttributeNamed:[_q key]];
1296 a = [[self entity] attributeNamed:[_q key]]; /* relationships ? */
1297 sql = [[self class] formatSQLString:sql format:[a readFormat]];
1298 [s appendString:sql];
1300 [s appendString:@" "];
1301 sql = [self sqlStringForSelector:[_q selector] value:v];
1302 [s appendString:sql];
1303 [s appendString:@" "];
1305 if (([_q selector] == EOQualifierOperatorLike) ||
1306 ([_q selector] == EOQualifierOperatorCaseInsensitiveLike))
1307 v = [[self class] sqlPatternFromShellPattern:v];
1309 sql = [self sqlStringForValue:v attributeNamed:[_q key]];
1310 [s appendString:sql];
1314 - (NSString *)sqlStringForNegatedQualifier:(EOQualifier *)_q {
1318 sql = [(id<EOQualifierSQLGeneration>)_q sqlStringForSQLExpression:self];
1319 s = [NSMutableString stringWithCapacity:[sql length] + 8];
1320 [s appendString:@"NOT ("];
1321 [s appendString:sql];
1322 [s appendString:@")"];
1326 - (NSString *)sqlStringForConjoinedQualifiers:(NSArray *)_qs {
1329 id (*objAtIdx)(id,SEL,unsigned);
1331 objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1332 for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1333 id<EOQualifierSQLGeneration> q;
1335 q = objAtIdx(self, @selector(objectAtIndex:), i);
1338 s = [NSMutableString stringWithCapacity:128];
1340 [s appendString:@" AND "];
1342 [s appendString:[q sqlStringForSQLExpression:self]];
1347 - (NSString *)sqlStringForDisjoinedQualifiers:(NSArray *)_qs {
1350 id (*objAtIdx)(id,SEL,unsigned);
1352 objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1353 for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1354 id<EOQualifierSQLGeneration> q;
1356 q = objAtIdx(self, @selector(objectAtIndex:), i);
1359 s = [NSMutableString stringWithCapacity:128];
1361 [s appendString:@" OR "];
1363 [s appendString:[q sqlStringForSQLExpression:self]];
1370 - (NSMutableString *)listString {
1371 if (self->listString == nil)
1372 self->listString = [[NSMutableString alloc] initWithCapacity:128];
1373 return self->listString;
1376 - (void)appendItem:(NSString *)_itemString toListString:(NSMutableString *)_ls {
1377 if ([_ls length] > 0)
1378 [_ls appendString:@","];
1379 [_ls appendString:_itemString];
1384 - (void)prepareDeleteExpressionForQualifier:(EOQualifier *)_qual {
1385 NSString *tableList, *sql;
1387 self->whereClauseString =
1388 [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1390 tableList = [self tableListWithRootEntity:[self entity]];
1392 sql = [self assembleDeleteStatementWithQualifier:_qual
1394 whereClause:[self whereClauseString]];
1396 [self setStatement:sql];
1401 - (void)addUpdateListAttribute:(EOAttribute *)_attr value:(NSString *)_value {
1404 s = [[NSMutableString alloc] initWithCapacity:32];
1405 [s appendString:[_attr columnName]];
1406 [s appendString:@"="];
1407 _value = [[self class] formatSQLString:_value format:[_attr writeFormat]];
1408 [s appendString:_value];
1410 [self appendItem:s toListString:[self listString]];
1414 - (void)prepareUpdateExpressionWithRow:(NSDictionary *)_row
1415 qualifier:(EOQualifier *)_qual
1419 NSString *tableList, *sql;
1421 keys = [_row keyEnumerator];
1422 while ((key = [keys nextObject])) {
1423 EOAttribute *attribute;
1426 attribute = [self->entity attributeNamed:key];
1427 value = [_row objectForKey:key];
1429 [self addUpdateListAttribute:attribute value:value];
1432 self->whereClauseString =
1433 [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1435 tableList = [self tableListWithRootEntity:[self entity]];
1437 sql = [self assembleUpdateStatementWithRow:_row
1440 updateList:[self listString]
1441 whereClause:[self whereClauseString]];
1443 [self setStatement:sql];
1446 @end /* EOSQLExpression(NewInEOF2) */