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"
29 #include "EOAdaptor.h"
30 #include "EOAdaptorChannel.h"
31 #include "EOAdaptorContext.h"
32 #include "EOAttribute.h"
33 #include "EOAttributeOrdering.h"
35 #include "EOFExceptions.h"
36 #include "EOSQLQualifier.h"
37 #include "EORelationship.h"
38 #include <EOControl/EOFetchSpecification.h>
39 #include <EOControl/EONull.h>
40 #include <EOControl/EOQualifier.h>
41 #include <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 - (NSString *)joinExpressionForRelationshipPaths:(NSArray *)relationshipPaths {
425 NSMutableString *expression;
426 NSEnumerator *enumerator;
427 NSMutableArray *rels;
428 NSArray *relationshipPath;
429 EORelationship *relationship;
432 expression = [NSMutableString stringWithCapacity:64];
433 enumerator = [relationshipPaths objectEnumerator];
434 rels = [[NSMutableArray alloc] initWithCapacity:4];
436 while ((relationshipPath = [enumerator nextObject]) != nil) {
437 NSEnumerator *componentRelationshipsEnumerator;
440 componentRelationshipsEnumerator = [relationshipPath objectEnumerator];
443 while((relationship = [componentRelationshipsEnumerator nextObject])) {
444 if (![rels containsObject:relationship]) {
445 id sourceAttribute, destinationAttribute;
447 [rels addObject:relationship];
449 /* Compute the SQL expression string corresponding to each join in
451 sourceAttribute = [self expressionValueForAttribute:
452 [relationship sourceAttribute]
454 destinationAttribute = [self expressionValueForAttribute:
455 [relationship destinationAttribute]
457 [relationship destinationEntity]];
459 //NSLog(@"sourceAttribute %@", sourceAttribute);
460 //NSLog(@"destinationAttribute %@", destinationAttribute);
465 [expression appendString:@" AND "];
467 [expression appendString:[sourceAttribute description]];
468 [expression appendString:@" = "];
469 [expression appendString:[destinationAttribute description]];
471 /* Compute the next context which is the current relationship. */
472 context = [relationship destinationEntity];
479 - (NSString *)orderByClauseForFetchOrder:(NSArray *)fetchOrder
482 NSMutableString *orderBy;
484 if ((count = [fetchOrder count]) == 0)
487 orderBy = (id)[NSMutableString stringWithCapacity:32];
488 for(i = 0; i < count; i++) {
491 eorder = [fetchOrder objectAtIndex:i];
493 if (i != 0) [orderBy appendString:@", "];
495 if ([eorder isKindOfClass:[EOSortOrdering class]]) {
496 EOSortOrdering *order;
497 EOAttribute *attribute;
502 ordering = [order selector];
503 attribute = [self->entity attributeNamed:[order key]];
505 if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
506 sel_eq(ordering, EOCompareCaseInsensitiveDescending))
511 [orderBy appendFormat:fmt, [self expressionValueForAttribute:attribute]];
513 if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
514 sel_eq(ordering, EOCompareAscending)) {
515 [orderBy appendString:@" ASC"];
517 else if (sel_eq(ordering, EOCompareCaseInsensitiveDescending) ||
518 sel_eq(ordering, EOCompareDescending)) {
519 [orderBy appendString:@" DESC"];
523 EOAttributeOrdering *order;
527 ordering = [order ordering];
529 [orderBy appendFormat:@"%@",
530 [self expressionValueForAttribute:[order attribute]]];
531 if (ordering != EOAnyOrder)
532 [orderBy appendString:
533 ([order ordering] == EOAscendingOrder ? @" ASC" : @" DESC")];
540 - (NSString *)literalForAttribute:(EOAttribute *)_attribute
541 withValue:(id)_value fromRow:(NSDictionary *)_row
543 return (self->adaptor)
544 ? [self->adaptor formatValue:_value?_value:null forAttribute:_attribute]
545 : [_value stringValue];
548 - (id)updateListForRow:(NSDictionary *)row {
549 PrintfFormatScanner *formatScanner = nil;
550 EOInsertUpdateScannerHandler *scannerHandler = nil;
551 NSMutableString *expression = nil;
552 NSEnumerator *enumerator;
553 NSString *attributeName = nil;
556 enumerator = [row keyEnumerator];
557 expression = [NSMutableString stringWithCapacity:256];
559 formatScanner = [[PrintfFormatScanner alloc] init];
560 AUTORELEASE(formatScanner);
561 scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
562 AUTORELEASE(scannerHandler);
564 [formatScanner setAllowOnlySpecifier:YES];
565 [formatScanner setFormatScannerHandler:scannerHandler];
567 while((attributeName = [enumerator nextObject])) {
568 EOAttribute *attribute;
569 NSString *columnName = nil;
572 attribute = [entity attributeNamed:attributeName];
574 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
576 ? [adaptor formatAttribute:attribute]
577 : [attribute columnName];
579 value = [row objectForKey:attributeName];
581 value = [self literalForAttribute:attribute
582 withValue:value fromRow:row];
584 if (first) first = NO;
585 else [expression appendString:@", "];
587 [expression appendString:columnName];
588 [expression appendString:@" = "];
589 [expression appendString:value];
595 - (id)columnListForRow:(NSDictionary *)row {
596 NSMutableString *expression;
597 NSEnumerator *enumerator;
598 NSString *attributeName = nil;
601 expression = [NSMutableString stringWithCapacity:128];
602 enumerator = [row keyEnumerator];
604 while ((attributeName = [enumerator nextObject])) {
605 EOAttribute *attribute;
606 NSString *columnName;
608 attribute = [entity attributeNamed:attributeName];
610 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
612 ? [adaptor formatAttribute:attribute]
613 : [attribute columnName];
615 if (first) first = NO;
616 else [expression appendString:@", "];
618 [expression appendString:columnName];
624 - (id)valueListForRow:(NSDictionary *)row {
625 EOInsertUpdateScannerHandler *scannerHandler;
626 PrintfFormatScanner *formatScanner;
627 NSEnumerator *enumerator;
628 NSString *attributeName = nil;
629 NSMutableString *expression = nil;
632 formatScanner = [[PrintfFormatScanner alloc] init];
633 AUTORELEASE(formatScanner);
634 scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
635 AUTORELEASE(scannerHandler);
637 expression = [NSMutableString stringWithCapacity:256];
638 enumerator = [row keyEnumerator];
640 [formatScanner setAllowOnlySpecifier:YES];
641 [formatScanner setFormatScannerHandler:scannerHandler];
643 while ((attributeName = [enumerator nextObject])) {
644 EOAttribute *attribute;
647 attribute = [entity attributeNamed:attributeName];
648 value = [row objectForKey:attributeName];
650 NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
651 value = [self literalForAttribute:attribute
652 withValue:value fromRow:row];
654 if (first) first = NO;
655 else [expression appendString:@", "];
657 [expression appendString:value];
663 - (NSArray *)relationshipPathsForAttributes:(NSArray *)attributes
664 qualifier:(EOSQLQualifier *)qualifier
665 fetchOrder:(NSArray *)fetchOrder
668 NSMutableSet *entities;
669 NSMutableSet *relationshipPaths;
670 NSEnumerator *enumerator;
671 id entityOrRelationship;
673 entities = [NSMutableSet set];
674 relationshipPaths = [NSMutableSet set];
676 NSAssert3(self->entity,
677 @"entity should be non nil (attrs=%@, qual=%@, order=%@)",
678 attributes, qualifier, fetchOrder);
680 for (i = 0, count = [attributes count]; i < count; i++) {
681 EOAttribute *attribute;
683 attribute = [attributes objectAtIndex:i];
685 if ([attribute entity] != entity) {
686 [[[InvalidAttributeException alloc]
687 initWithFormat:@"all attributes must be from the same "
688 @"entity (attribute '%@' is not in '%@')",
690 [entity name]] raise];
693 /* attribute is normal. */
694 [entities addObject:[attribute entity]];
697 [relationshipPaths unionSet:[qualifier relationshipPaths]];
698 [entities unionSet:[qualifier additionalEntities]];
700 for (i = 0, count = [fetchOrder count]; i < count; i++) {
701 EOAttribute *attribute;
704 eorder = [fetchOrder objectAtIndex:i];
706 attribute = ([eorder isKindOfClass:[EOSortOrdering class]])
707 ? [self->entity attributeNamed:[(EOSortOrdering *)eorder key]]
708 : [(EOAttributeOrdering *)eorder attribute];
710 if ([attribute entity] != entity) {
711 [[[InvalidAttributeException alloc]
712 initWithFormat:@"all attributes must be from the same "
713 @"entity (attribute '%@' is not in '%@')",
715 [entity name]] raise];
719 entitiesAndPropertiesAliases = [NSMutableDictionary new];
720 fromListEntities = [NSMutableArray new];
721 enumerator = [entities objectEnumerator];
723 while ((entityOrRelationship = [enumerator nextObject])) {
724 NSString* alias = [NSString stringWithFormat:@"t%d", i++];
726 [entitiesAndPropertiesAliases setObject:alias
727 forKey:entityOrRelationship];
728 [fromListEntities addObject:entityOrRelationship];
731 return [relationshipPaths allObjects];
734 - (EOEntity *)entity {
737 - (id)finishBuildingExpression {
740 - (NSString *)lockClause {
744 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute
747 NSString *alias = [entitiesAndPropertiesAliases objectForKey:context];
748 NSString *columnName = nil;
751 ? [adaptor formatAttribute:attribute]
752 : [attribute columnName];
755 ? [NSString stringWithFormat:@"%@.%@", alias, columnName]
759 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute {
760 /* attribute is a normal attribute. Its alias is the alias
762 return [self expressionValueForAttribute:attribute
763 context:[attribute entity]];
766 - (NSString *)expressionValueForAttributePath:(NSArray *)definitionArray {
767 /* Take the alias of the last relationship. */
772 = [definitionArray objectAtIndex:([definitionArray count] - 2)];
773 alias = [entitiesAndPropertiesAliases objectForKey:relationship];
775 return AUTORELEASE(([[NSString alloc]
776 initWithFormat:@"%@.%@",
777 alias, [[definitionArray lastObject] columnName]]));
780 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)context {
781 return self->content;
784 + (Class)selectExpressionClass {
785 return [EOSelectSQLExpression class];
787 + (Class)insertExpressionClass {
788 return [EOInsertSQLExpression class];
790 + (Class)deleteExpressionClass {
791 return [EODeleteSQLExpression class];
793 + (Class)updateExpressionClass {
794 return [EOUpdateSQLExpression class];
797 - (EOAdaptor *)adaptor {
798 return self->adaptor;
803 - (NSString *)description {
806 ms = [NSMutableString stringWithCapacity:128];
807 [ms appendFormat:@"<0x%08X[%@]:\n", self, NSStringFromClass([self class])];
809 if (self->entity) [ms appendFormat:@" entity=%@\n", self->entity];
810 if (self->adaptor) [ms appendFormat:@" adaptor=%@\n", self->adaptor];
811 if (self->content) [ms appendFormat:@" content='%@\n'", self->content];
813 if (self->entitiesAndPropertiesAliases)
814 [ms appendFormat:@" aliases=%@\n", self->entitiesAndPropertiesAliases];
815 if (self->fromListEntities)
816 [ms appendFormat:@" from-entities=%@\n", self->fromListEntities];
818 if (self->whereClauseString)
819 [ms appendFormat:@" where=%@\n", self->whereClauseString];
821 if (self->listString) [ms appendFormat:@" list=%@\n", self->listString];
822 if (self->bindings) [ms appendFormat:@" bindings=%@\n", self->bindings];
824 [ms appendString:@">"];
828 @end /* EOSQLExpression */
831 @implementation EOInsertSQLExpression
832 @end /* EOInsertSQLExpression */
834 @implementation EOUpdateSQLExpression
835 @end /* EOUpdateSQLExpression */
837 @implementation EODeleteSQLExpression
838 @end /* EODeleteSQLExpression */
840 @implementation EOSQLExpression(NewInEOF2)
842 + (EOSQLExpression *)selectStatementForAttributes:(NSArray *)_attributes
844 fetchSpecification:(EOFetchSpecification *)_fspec
845 entity:(EOEntity *)_entity
848 [NSException raise:NSInvalidArgumentException
849 format:@"missing fetch specification argument .."];
851 if ([_attributes count] == 0) {
852 [NSException raise:NSInvalidArgumentException
853 format:@"missing attributes for select .."];
856 return [self selectExpressionForAttributes:_attributes
858 qualifier:[[_fspec qualifier] sqlQualifierForEntity:_entity]
859 fetchOrder:[_fspec sortOrderings]
863 + (EOSQLExpression *)expressionForString:(NSString *)_sql {
866 se = [[EOSQLExpression alloc] init];
867 [se setStatement:_sql];
868 return AUTORELEASE(se);
873 - (void)setStatement:(NSString *)_stmt {
876 self->content = [_stmt mutableCopy];
879 - (NSString *)statement {
880 return self->content;
883 - (NSString *)whereClauseString {
884 return self->whereClauseString;
889 - (NSString *)tableListWithRootEntity:(EOEntity *)_entity {
890 return ([self->fromListEntities count] > 0)
892 : [_entity externalName];
897 - (NSString *)assembleDeleteStatementWithQualifier:(EOQualifier *)_qualifier
898 tableList:(NSString *)_tableList
899 whereClause:(NSString *)_whereClause
903 s = [NSMutableString stringWithCapacity:64];
904 [s appendString:@"DELETE FROM "];
905 [s appendString:_tableList];
906 [s appendString:@" WHERE "];
907 [s appendString:_whereClause];
911 - (NSString *)assembleInsertStatementWithRow:(NSDictionary *)_row
912 tableList:(NSString *)_tables
913 columnList:(NSString *)_columns
914 valueList:(NSString *)_values
918 s = [NSMutableString stringWithCapacity:256];
919 [s appendString:@"INSERT INTO "];
920 [s appendString:_tables];
921 [s appendString:@" ("];
922 [s appendString:_columns];
923 [s appendString:@") VALUES ("];
924 [s appendString:_values];
925 [s appendString:@")"];
929 - (NSString *)assembleSelectStatementWithAttributes:(NSArray *)_attributes
931 qualifier:(EOQualifier *)_qualifier
932 fetchOrder:(NSArray *)_fetchOrder
933 selectString:(NSString *)_selectString
934 columnList:(NSString *)_columns
935 tableList:(NSString *)_tables
936 whereClause:(NSString *)_whereClause
937 joinClause:(NSString *)_joinClause
938 orderByClause:(NSString *)_orderByClause
939 lockClause:(NSString *)_lockClause
945 #warning DEBUG LOG, REMOVE!
946 [self logWithFormat:@"%s: '%@': %@", __PRETTY_FUNCTION__, _whereClause,self];
949 s = [NSMutableString stringWithCapacity:256];
951 [s appendString:_selectString ? _selectString : @"SELECT"];
952 [s appendString:@" "];
953 [s appendString:_columns];
954 [s appendString:@" FROM "];
955 [s appendString:_tables];
957 if ([_lockClause length] > 0) {
958 [s appendString:@" "];
959 [s appendString:_lockClause];
962 wlen = [_whereClause length];
963 jlen = [_joinClause length];
965 if ((wlen > 0) || (jlen > 0))
966 [s appendString:@" WHERE "];
969 [s appendString:_whereClause];
971 if ((wlen > 0) && (jlen > 0))
972 [s appendString:@" AND "];
975 [s appendString:_joinClause];
977 if ([_orderByClause length] > 0) {
978 [s appendString:@" ORDER BY "];
979 [s appendString:_orderByClause];
985 - (NSString *)assembleUpdateStatementWithRow:(NSDictionary *)_row
986 qualifier:(EOQualifier *)_qualifier
987 tableList:(NSString *)_tables
988 updateList:(NSString *)_updates
989 whereClause:(NSString *)_whereClause
993 s = [NSMutableString stringWithCapacity:256];
995 [s appendString:@"UPDATE "];
996 [s appendString:_tables];
997 [s appendString:@" SET "];
998 [s appendString:_updates];
999 [s appendString:@" WHERE "];
1000 [s appendString:_whereClause];
1005 - (NSString *)assembleJoinClauseWithLeftName:(NSString *)_leftName
1006 rightName:(NSString *)_rightName
1007 joinSemantic:(EOJoinSemantic)_semantic
1011 s = [NSMutableString stringWithCapacity:64];
1012 [s appendString:_leftName];
1013 switch (_semantic) {
1015 [s appendString:@" = "];
1017 case EOFullOuterJoin:
1018 [s appendString:@" *=* "];
1020 case EOLeftOuterJoin:
1021 [s appendString:@" *= "];
1023 case EORightOuterJoin:
1024 [s appendString:@" =* "];
1027 [s appendString:_rightName];
1033 - (NSString *)sqlStringForAttribute:(EOAttribute *)_attribute {
1034 NSLog(@"ERROR(%s): subclasses need to override this method!",
1035 __PRETTY_FUNCTION__);
1039 - (NSString *)sqlStringForAttributePath:(NSString *)_attrPath {
1040 NSLog(@"ERROR(%s): subclasses need to override this method!",
1041 __PRETTY_FUNCTION__);
1045 - (NSString *)sqlStringForAttributeNamed:(NSString *)_attrName {
1048 if ((a = [[self entity] attributeNamed:_attrName]))
1049 return [self sqlStringForAttribute:a];
1051 return [self sqlStringForAttributePath:_attrName];
1054 /* bind variables */
1056 + (BOOL)useBindVariables {
1059 - (BOOL)mustUseBindVariableForAttribute:(EOAttribute *)_attr {
1062 - (BOOL)shouldUseBindVariableForAttribute:(EOAttribute *)_attr {
1066 - (NSMutableDictionary *)bindVariableDictionaryForAttribute:(EOAttribute *)_attr
1069 NSMutableDictionary *d;
1071 d = [NSMutableDictionary dictionaryWithCapacity:8];
1072 [d setObject:_attr forKey:EOBindVariableAttributeKey];
1073 [d setObject:_value ? _value : null forKey:EOBindVariableValueKey];
1077 - (void)addBindVariableDictionary:(NSMutableDictionary *)_dictionary {
1078 if (self->bindings == nil)
1079 self->bindings = [[NSMutableArray alloc] init];
1081 - (NSArray *)bindVariableDictionaries {
1082 return self->bindings;
1087 + (NSString *)formatValue:(id)_value forAttribute:(EOAttribute *)_attribute {
1088 return _value ? _value : null;
1091 - (NSString *)sqlStringForValue:(id)_value attributeNamed:(NSString *)_attrName {
1092 NSMutableDictionary *bindVars;
1093 EOAttribute *attribute;
1095 attribute = [[self entity] attributeNamed:_attrName];
1097 if ([self mustUseBindVariableForAttribute:attribute])
1098 bindVars = [self bindVariableDictionaryForAttribute:attribute value:_value];
1099 else if ([[self class] useBindVariables] &&
1100 [self shouldUseBindVariableForAttribute:attribute]) {
1101 bindVars = [self bindVariableDictionaryForAttribute:attribute value:_value];
1107 [self addBindVariableDictionary:bindVars];
1108 return [bindVars objectForKey:EOBindVariablePlaceHolderKey];
1111 return [[self class] formatValue:_value?_value:null forAttribute:attribute];
1114 + (NSString *)sqlPatternFromShellPattern:(NSString *)_pattern {
1117 if ((len = [_pattern length]) > 0) {
1118 unsigned cstrLen = [_pattern cStringLength];
1119 char cstrBuf[cstrLen + 1];
1121 char buf[len * 3 + 1];
1123 BOOL didSomething = NO;
1125 [_pattern getCString:cstrBuf];
1128 for (i = 0; *cstr; cstr++) {
1131 buf[i] = '%'; i++; didSomething = YES;
1134 buf[i] = '_'; i++; didSomething = YES;
1159 return (didSomething)
1160 ? [NSString stringWithCString:buf length:i]
1168 + (NSString *)formatSQLString:(NSString *)_sqlString format:(NSString *)_fmt {
1172 /* qualifier operators */
1174 - (NSString *)sqlStringForSelector:(SEL)_selector value:(id)_value {
1175 if ((_value == null) || (_value == nil)) {
1176 if (sel_eq(_selector, EOQualifierOperatorEqual))
1178 else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1182 if (sel_eq(_selector, EOQualifierOperatorEqual))
1184 else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1188 if (sel_eq(_selector, EOQualifierOperatorLessThan))
1190 else if (sel_eq(_selector, EOQualifierOperatorGreaterThan))
1192 else if (sel_eq(_selector, EOQualifierOperatorLessThanOrEqualTo))
1194 else if (sel_eq(_selector, EOQualifierOperatorGreaterThanOrEqualTo))
1196 else if (sel_eq(_selector, EOQualifierOperatorLike))
1199 return [NSString stringWithFormat:@"UNKNOWN<%@>",
1200 NSStringFromSelector(_selector)];
1206 - (NSString *)sqlStringForKeyComparisonQualifier:(EOKeyComparisonQualifier *)_q {
1211 s = [NSMutableString stringWithCapacity:64];
1213 sql = [self sqlStringForAttributeNamed:[_q leftKey]];
1214 a = [[self entity] attributeNamed:[_q leftKey]]; /* relationships ? */
1215 sql = [[self class] formatSQLString:sql format:nil];
1216 [s appendString:sql];
1218 [s appendString:@" "];
1219 [s appendString:[self sqlStringForSelector:[_q selector] value:nil]];
1220 [s appendString:@" "];
1222 sql = [self sqlStringForAttributeNamed:[_q rightKey]];
1223 a = [[self entity] attributeNamed:[_q rightKey]]; /* relationships ? */
1224 sql = [[self class] formatSQLString:sql format:nil];
1225 [s appendString:sql];
1230 - (NSString *)sqlStringForKeyValueQualifier:(EOKeyValueQualifier *)_q {
1237 s = [NSMutableString stringWithCapacity:64];
1239 sql = [self sqlStringForAttributeNamed:[_q key]];
1240 a = [[self entity] attributeNamed:[_q key]]; /* relationships ? */
1241 sql = [[self class] formatSQLString:sql format:nil];
1242 [s appendString:sql];
1244 [s appendString:@" "];
1245 sql = [self sqlStringForSelector:[_q selector] value:v];
1246 [s appendString:sql];
1247 [s appendString:@" "];
1249 if (([_q selector] == EOQualifierOperatorLike) ||
1250 ([_q selector] == EOQualifierOperatorCaseInsensitiveLike))
1251 v = [[self class] sqlPatternFromShellPattern:v];
1253 sql = [self sqlStringForValue:v attributeNamed:[_q key]];
1254 [s appendString:sql];
1258 - (NSString *)sqlStringForNegatedQualifier:(EOQualifier *)_q {
1262 sql = [(id<EOQualifierSQLGeneration>)_q sqlStringForSQLExpression:self];
1263 s = [NSMutableString stringWithCapacity:[sql length] + 8];
1264 [s appendString:@"NOT ("];
1265 [s appendString:sql];
1266 [s appendString:@")"];
1270 - (NSString *)sqlStringForConjoinedQualifiers:(NSArray *)_qs {
1273 id (*objAtIdx)(id,SEL,unsigned);
1275 objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1276 for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1277 id<EOQualifierSQLGeneration> q;
1279 q = objAtIdx(self, @selector(objectAtIndex:), i);
1282 s = [NSMutableString stringWithCapacity:128];
1284 [s appendString:@" AND "];
1286 [s appendString:[q sqlStringForSQLExpression:self]];
1291 - (NSString *)sqlStringForDisjoinedQualifiers:(NSArray *)_qs {
1294 id (*objAtIdx)(id,SEL,unsigned);
1296 objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1297 for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1298 id<EOQualifierSQLGeneration> q;
1300 q = objAtIdx(self, @selector(objectAtIndex:), i);
1303 s = [NSMutableString stringWithCapacity:128];
1305 [s appendString:@" OR "];
1307 [s appendString:[q sqlStringForSQLExpression:self]];
1314 - (NSMutableString *)listString {
1315 if (self->listString == nil)
1316 self->listString = [[NSMutableString alloc] initWithCapacity:128];
1317 return self->listString;
1320 - (void)appendItem:(NSString *)_itemString toListString:(NSMutableString *)_ls {
1321 if ([_ls length] > 0)
1322 [_ls appendString:@","];
1323 [_ls appendString:_itemString];
1328 - (void)prepareDeleteExpressionForQualifier:(EOQualifier *)_qual {
1329 NSString *tableList, *sql;
1331 self->whereClauseString =
1332 [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1334 tableList = [self tableListWithRootEntity:[self entity]];
1336 sql = [self assembleDeleteStatementWithQualifier:_qual
1338 whereClause:[self whereClauseString]];
1340 [self setStatement:sql];
1345 - (void)addUpdateListAttribute:(EOAttribute *)_attr value:(NSString *)_value {
1348 s = [[NSMutableString alloc] initWithCapacity:32];
1349 [s appendString:[_attr columnName]];
1350 [s appendString:@"="];
1351 _value = [[self class] formatSQLString:_value format:nil];
1352 [s appendString:_value];
1354 [self appendItem:s toListString:[self listString]];
1358 - (void)prepareUpdateExpressionWithRow:(NSDictionary *)_row
1359 qualifier:(EOQualifier *)_qual
1363 NSString *tableList, *sql;
1365 keys = [_row keyEnumerator];
1366 while ((key = [keys nextObject])) {
1367 EOAttribute *attribute;
1370 attribute = [self->entity attributeNamed:key];
1371 value = [_row objectForKey:key];
1373 [self addUpdateListAttribute:attribute value:value];
1376 self->whereClauseString =
1377 [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1379 tableList = [self tableListWithRootEntity:[self entity]];
1381 sql = [self assembleUpdateStatementWithRow:_row
1384 updateList:[self listString]
1385 whereClause:[self whereClauseString]];
1387 [self setStatement:sql];
1390 @end /* EOSQLExpression(NewInEOF2) */