]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOSQLExpression.m
added missing inline pathes
[sope] / sope-gdl1 / GDLAccess / EOSQLExpression.m
1 /* 
2    EOSQLExpression.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
7    Date: September 1996
8
9    This file is part of the GNUstep Database Library.
10
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.
15
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.
20
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.
25 */
26
27 #include "EOSQLExpression.h"
28 #include "common.h"
29 #include "EOAdaptor.h"
30 #include "EOAdaptorChannel.h"
31 #include "EOAdaptorContext.h"
32 #include "EOAttribute.h"
33 #include "EOAttributeOrdering.h"
34 #include "EOEntity.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>
42
43 #if LIB_FOUNDATION_LIBRARY
44 #  include <extensions/DefaultScannerHandler.h>
45 #  include <extensions/PrintfFormatScanner.h>
46 #else
47 #  include "DefaultScannerHandler.h"
48 #  include "PrintfFormatScanner.h"
49 #endif
50
51 /*
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
54 from entity.
55
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.
61
62 The algorithm uses a dictionary that has as keys either entities or
63 relationships. The values in dictionary are aliases.
64
65 For a simple attribute we insert in the above dictionary the attribute's entity
66 as key and an alias for it.
67
68 For a flattened attribute with the following definition: `toEntity1.toEntity2.
69 ... toEntityn.attribute', we have to assign to each toEntityi object an alias.
70
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
76 rules.
77
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.
83
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
89
90 context-alias.source-attribute join-operator
91             relationship-alias.destination-attribute
92
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.
96
97 */
98
99 static EONull *null = nil;
100 NSString *EOBindVariableNameKey        = @"name";
101 NSString *EOBindVariablePlaceHolderKey = @"placeHolder";
102 NSString *EOBindVariableAttributeKey   = @"attribute";
103 NSString *EOBindVariableValueKey       = @"value";
104
105
106 @interface EOInsertUpdateScannerHandler : DefaultScannerHandler
107 {
108     NSString    *value;
109     EOAttribute *attribute;
110     EOAdaptor   *adaptor;
111 }
112
113 - (void)setValue:(NSString*)value
114   attribute:(EOAttribute*)attribute
115   adaptor:(EOAdaptor*)adaptor;
116 @end
117
118
119 @implementation EOInsertUpdateScannerHandler
120
121 - (id)init {
122   [super init];
123
124   specHandler['V']
125     = [self methodForSelector:@selector(convertValue:scanner:)];
126   return self;
127 }
128
129 - (void)dealloc {
130   RELEASE(self->value);
131   RELEASE(self->attribute);
132   RELEASE(self->adaptor);
133   [super dealloc];
134 }
135
136 - (void)setValue:(NSString *)_value
137   attribute:(EOAttribute*)_attribute
138   adaptor:(EOAdaptor*)_adaptor
139 {
140     ASSIGNCOPY(self->value,     _value);
141     ASSIGN(self->attribute, _attribute);
142     ASSIGN(self->adaptor,   _adaptor);
143 }
144
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]
149       : self->value;
150 }
151
152 @end /* EOInsertUpdateScannerHandler */
153
154
155 @implementation EOSQLExpression
156
157 + (int)version {
158   return 1;
159 }
160
161 + (void)initialize {
162   if (null == nil) null = [[NSNull null] retain];
163 }
164
165 - (id)initWithEntity:(EOEntity *)_entity {
166   if ((self = [super init])) {
167     ASSIGN(self->entity, _entity);
168   }
169   return self;
170 }
171 - (id)init {
172   return [self initWithEntity:nil];
173 }
174
175 + (id)deleteExpressionWithQualifier:(EOSQLQualifier*)qualifier
176   channel:(EOAdaptorChannel*)channel
177 {
178     EOSQLExpression* sqlExpression
179         = AUTORELEASE([[self deleteExpressionClass] new]);
180     [sqlExpression deleteExpressionWithQualifier:qualifier channel:channel];
181     return sqlExpression;
182 }
183 + (id)insertExpressionForRow:(NSDictionary *)row
184   entity:(EOEntity*)_entity
185   channel:(EOAdaptorChannel*)channel
186 {
187     EOSQLExpression* sqlExpression
188         = AUTORELEASE([[self insertExpressionClass] new]);
189     
190     [sqlExpression insertExpressionForRow:row entity:_entity channel:channel];
191     return sqlExpression;
192 }
193
194 - (id)deleteExpressionWithQualifier:(EOSQLQualifier *)qualifier
195   channel:(EOAdaptorChannel*)channel
196 {
197   NSString *sql;
198   
199   self = [self initWithEntity:[qualifier entity]];
200   
201   self->adaptor = RETAIN([[channel adaptorContext] adaptor]);
202
203   sql = [self assembleDeleteStatementWithQualifier:qualifier
204               tableList:[entity externalName]
205               whereClause:[self whereClauseForQualifier:qualifier]];
206   
207   self->content = [sql mutableCopy];
208   [self finishBuildingExpression];
209   
210   return self;
211 }
212
213 - (id)insertExpressionForRow:(NSDictionary *)row
214   entity:(EOEntity*)_entity
215   channel:(EOAdaptorChannel*)channel
216 {
217   NSString *sql;
218   
219   self = [self initWithEntity:_entity];
220
221   self->adaptor = RETAIN([[channel adaptorContext] adaptor]);
222
223   sql = [self assembleInsertStatementWithRow:row
224               tableList:[entity externalName]
225               columnList:[self columnListForRow:row]
226               valueList:[self valueListForRow:row]];
227   
228   self->content = [sql mutableCopy];
229   
230   [self finishBuildingExpression];
231
232   return self;
233 }
234
235 + (id)selectExpressionForAttributes:(NSArray *)attributes
236   lock:(BOOL)flag
237   qualifier:(EOSQLQualifier *)qualifier
238   fetchOrder:(NSArray *)fetchOrder
239   channel:(EOAdaptorChannel *)channel
240 {
241     EOSQLExpression* sqlExpression
242         = AUTORELEASE([[self selectExpressionClass] new]);
243     [sqlExpression selectExpressionForAttributes:attributes
244                     lock:flag
245                     qualifier:qualifier
246                     fetchOrder:fetchOrder
247                     channel:channel];
248     return sqlExpression;
249 }
250
251 - (id)selectExpressionForAttributes:(NSArray *)attributes
252   lock:(BOOL)flag
253   qualifier:(EOSQLQualifier *)qualifier
254   fetchOrder:(NSArray *)fetchOrder
255   channel:(EOAdaptorChannel *)channel
256 {
257     NSArray  *relationshipPaths;
258     NSString *sql;
259     
260     self = [self initWithEntity:[qualifier entity]];
261     
262     self->adaptor = [[[channel adaptorContext] adaptor] retain];
263     // self->content = [NSMutableString new]; // mh: BUG!!!
264     
265     relationshipPaths = [self relationshipPathsForAttributes:attributes
266                               qualifier:qualifier
267                               fetchOrder:fetchOrder];
268     
269     sql = [self assembleSelectStatementWithAttributes:attributes
270                 lock:flag
271                 qualifier:qualifier
272                 fetchOrder:fetchOrder
273                 selectString:
274                   [qualifier usesDistinct] ? @"SELECT DISTINCT" : @"SELECT"
275                 columnList:
276                   [self selectListWithAttributes:attributes 
277                         qualifier:qualifier]
278                 tableList:[self fromClause]
279                 whereClause:[self whereClauseForQualifier:qualifier]
280                 joinClause:
281                   [self joinExpressionForRelationshipPaths:relationshipPaths]
282                 orderByClause:[self orderByClauseForFetchOrder:fetchOrder]
283                 lockClause:flag ? [self lockClause] : nil];
284     
285     self->content = [sql mutableCopy];
286     
287     [self finishBuildingExpression];
288     
289     return self;
290 }
291 + (id)updateExpressionForRow:(NSDictionary *)row
292   qualifier:(EOSQLQualifier *)qualifier
293   channel:(EOAdaptorChannel *)channel
294 {
295     EOSQLExpression* sqlExpression
296         = AUTORELEASE([[self updateExpressionClass] new]);
297     [sqlExpression updateExpressionForRow:row
298                    qualifier:qualifier
299                    channel:channel];
300     return sqlExpression;
301 }
302
303 - (id)updateExpressionForRow:(NSDictionary *)row
304   qualifier:(EOSQLQualifier *)qualifier
305   channel:(EOAdaptorChannel *)channel
306 {
307   NSString *sql;
308
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];
314   }
315   
316   self->adaptor = [[[channel adaptorContext] adaptor] retain];
317   self->content = [[NSMutableString alloc] init];
318
319   sql = [self assembleUpdateStatementWithRow:row
320               qualifier:qualifier
321               tableList:[entity externalName]
322               updateList:[self updateListForRow:row]
323               whereClause:[self whereClauseForQualifier:qualifier]];
324   [self->content appendString:sql];
325   
326   [self finishBuildingExpression];
327
328   return self;
329 }
330
331 - (void)dealloc {
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);
340   [super dealloc];
341 }
342
343 - (NSString *)selectListWithAttributes:(NSArray *)attributes
344   qualifier:(EOSQLQualifier *)qualifier
345 {
346     NSMutableString *selectList;
347     NSEnumerator    *enumerator;
348     BOOL            first = YES;
349     EOAttribute     *attribute;
350     BOOL            isDistinct;
351
352     selectList = [NSMutableString stringWithCapacity:128];
353     isDistinct = [qualifier usesDistinct];
354
355     /* check whether DISTINCT is allowed */
356     enumerator = [attributes objectEnumerator];
357     while ((attribute = [enumerator nextObject])) {
358       if (self->adaptor) {
359         if (![self->adaptor attributeAllowedInDistinctSelects:attribute]) {
360 #if DEBUG && 0
361           NSLog(@"WARNING: tried to select attribute type %@ with DISTINCT "
362                 @"which is not allowed, disabling DISTINCT: %@",
363                 [attribute externalType], [attribute name]);
364 #endif
365           isDistinct = NO;
366           break;
367         }
368       }
369     }
370     
371     enumerator = [attributes objectEnumerator];
372     while ((attribute = [enumerator nextObject])) {
373         if(first)
374             first = NO;
375         else
376             [selectList appendString:@", "];
377
378         [selectList appendString:[self expressionValueForAttribute:attribute]];
379     }
380
381     return selectList;
382 }
383
384 - (NSString *)fromClause {
385     NSMutableString *fromClause;
386     NSEnumerator *enumerator;
387     BOOL first = YES;
388     id key;
389
390     fromClause = [NSMutableString stringWithCapacity:64];
391     enumerator = [self->fromListEntities objectEnumerator];
392     
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])) {
398         if (first)
399             first = NO;
400         else
401             [fromClause appendString:@", "];
402
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]];
408         }
409         else {
410             /* This is an EOEntity. */
411             [fromClause appendFormat:@"%@ %@",
412                             [key externalName],
413                             [entitiesAndPropertiesAliases objectForKey:key]];
414         }
415     }
416
417     return fromClause;
418 }
419
420 - (NSString *)whereClauseForQualifier:(EOSQLQualifier *)_qualifier {
421   return [_qualifier expressionValueForContext:self];
422 }
423
424 - (NSString *)joinExpressionForRelationshipPaths:(NSArray *)relationshipPaths {
425   NSMutableString *expression;
426   NSEnumerator    *enumerator;
427   NSMutableArray  *rels;
428   NSArray         *relationshipPath;
429   EORelationship  *relationship;
430   BOOL            first = YES;
431
432   expression = [NSMutableString stringWithCapacity:64];
433   enumerator = [relationshipPaths objectEnumerator];
434   rels       = [[NSMutableArray alloc] initWithCapacity:4];
435     
436   while ((relationshipPath = [enumerator nextObject]) != nil) {
437     NSEnumerator *componentRelationshipsEnumerator;
438     id context;
439
440     componentRelationshipsEnumerator = [relationshipPath objectEnumerator];
441     context = entity;
442
443     while((relationship = [componentRelationshipsEnumerator nextObject])) {
444       if (![rels containsObject:relationship]) {
445         id sourceAttribute, destinationAttribute;
446
447         [rels addObject:relationship];
448             
449         /* Compute the SQL expression string corresponding to each join in
450            the relationship. */
451         sourceAttribute = [self expressionValueForAttribute:
452                                 [relationship sourceAttribute]
453                                 context:context];
454         destinationAttribute = [self expressionValueForAttribute:
455                                      [relationship destinationAttribute]
456                                      context:
457                                      [relationship destinationEntity]];
458
459         //NSLog(@"sourceAttribute %@", sourceAttribute);
460         //NSLog(@"destinationAttribute %@", destinationAttribute);
461
462         if (first) 
463           first = NO;
464         else
465           [expression appendString:@" AND "];
466
467         [expression appendString:[sourceAttribute description]];
468         [expression appendString:@" = "];
469         [expression appendString:[destinationAttribute description]];
470       }
471       /* Compute the next context which is the current relationship. */
472       context = [relationship destinationEntity];
473     }
474   }
475   [rels release];
476   return expression;
477 }
478
479 - (NSString *)orderByClauseForFetchOrder:(NSArray *)fetchOrder
480 {
481   int             i, count;
482   NSMutableString *orderBy;
483   
484   if ((count = [fetchOrder count]) == 0)
485     return @"";
486   
487   orderBy = (id)[NSMutableString stringWithCapacity:32];
488   for(i = 0; i < count; i++) {
489     id eorder;
490
491     eorder = [fetchOrder objectAtIndex:i];
492     
493     if (i != 0) [orderBy appendString:@", "];
494     
495     if ([eorder isKindOfClass:[EOSortOrdering class]]) {
496       EOSortOrdering *order;
497       EOAttribute    *attribute;
498       SEL            ordering;
499       NSString       *fmt;
500       
501       order     = eorder;
502       ordering  = [order selector];
503       attribute = [self->entity attributeNamed:[order key]];
504
505       if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
506           sel_eq(ordering, EOCompareCaseInsensitiveDescending))
507         fmt = @"LOWER(%@)";
508       else
509         fmt = @"%@";
510       
511       [orderBy appendFormat:fmt, [self expressionValueForAttribute:attribute]];
512
513       if (sel_eq(ordering, EOCompareCaseInsensitiveAscending) ||
514           sel_eq(ordering, EOCompareAscending)) {
515         [orderBy appendString:@" ASC"];
516       }
517       else if (sel_eq(ordering, EOCompareCaseInsensitiveDescending) ||
518                sel_eq(ordering, EOCompareDescending)) {
519         [orderBy appendString:@" DESC"];
520       }
521     }
522     else {
523       EOAttributeOrdering *order;
524       EOOrdering ordering;
525       
526       order    = eorder;
527       ordering = [order ordering];
528       
529       [orderBy appendFormat:@"%@",
530                [self expressionValueForAttribute:[order attribute]]];
531       if (ordering != EOAnyOrder)
532         [orderBy appendString:
533                  ([order ordering] == EOAscendingOrder ? @" ASC" : @" DESC")];
534     }
535   }
536
537   return orderBy;
538 }
539
540 - (NSString *)literalForAttribute:(EOAttribute *)_attribute
541   withValue:(id)_value fromRow:(NSDictionary *)_row
542 {
543   return (self->adaptor)
544     ? [self->adaptor formatValue:_value?_value:null forAttribute:_attribute]
545     : [_value stringValue];
546 }
547
548 - (id)updateListForRow:(NSDictionary *)row {
549   PrintfFormatScanner          *formatScanner  = nil;
550   EOInsertUpdateScannerHandler *scannerHandler = nil;
551   NSMutableString *expression    = nil;
552   NSEnumerator    *enumerator;
553   NSString        *attributeName = nil;
554   BOOL            first          = YES;
555
556   enumerator    = [row keyEnumerator];
557   expression = [NSMutableString stringWithCapacity:256];
558     
559   formatScanner = [[PrintfFormatScanner alloc] init];
560   AUTORELEASE(formatScanner);
561   scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
562   AUTORELEASE(scannerHandler);
563
564   [formatScanner setAllowOnlySpecifier:YES];
565   [formatScanner setFormatScannerHandler:scannerHandler];
566
567   while((attributeName = [enumerator nextObject])) {
568     EOAttribute *attribute;
569     NSString    *columnName   = nil;
570     id          value         = nil;
571
572     attribute = [entity attributeNamed:attributeName];
573         
574     NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
575     columnName = adaptor
576       ? [adaptor formatAttribute:attribute]
577       : [attribute columnName];
578
579     value = [row objectForKey:attributeName];
580         
581     value = [self literalForAttribute:attribute
582                   withValue:value fromRow:row];
583     
584     if (first) first = NO;
585     else [expression appendString:@", "];
586
587     [expression appendString:columnName];
588     [expression appendString:@" = "];
589     [expression appendString:value];
590   }
591
592   return expression;
593 }
594
595 - (id)columnListForRow:(NSDictionary *)row {
596   NSMutableString *expression;
597   NSEnumerator    *enumerator;
598   NSString        *attributeName = nil;
599   BOOL            first          = YES;
600
601   expression = [NSMutableString stringWithCapacity:128];
602   enumerator = [row keyEnumerator];
603     
604   while ((attributeName = [enumerator nextObject])) {
605     EOAttribute *attribute;
606     NSString    *columnName;
607
608     attribute = [entity attributeNamed:attributeName];
609         
610     NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
611     columnName = adaptor
612       ? [adaptor formatAttribute:attribute]
613       : [attribute columnName];
614
615     if (first) first = NO;
616     else [expression appendString:@", "];
617
618     [expression appendString:columnName];
619   }
620     
621   return expression;
622 }
623
624 - (id)valueListForRow:(NSDictionary *)row  {
625   EOInsertUpdateScannerHandler *scannerHandler;
626   PrintfFormatScanner          *formatScanner;
627   NSEnumerator                 *enumerator;
628   NSString                     *attributeName  = nil;
629   NSMutableString              *expression     = nil;
630   BOOL                         first           = YES;
631
632   formatScanner = [[PrintfFormatScanner alloc] init];
633   AUTORELEASE(formatScanner);
634   scannerHandler = [[EOInsertUpdateScannerHandler alloc] init];
635   AUTORELEASE(scannerHandler);
636
637   expression = [NSMutableString stringWithCapacity:256];
638   enumerator     = [row keyEnumerator];
639
640   [formatScanner setAllowOnlySpecifier:YES];
641   [formatScanner setFormatScannerHandler:scannerHandler];
642
643   while ((attributeName = [enumerator nextObject])) {
644     EOAttribute *attribute;
645     id          value;
646
647     attribute    = [entity attributeNamed:attributeName];
648     value        = [row objectForKey:attributeName];
649         
650     NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
651     value = [self literalForAttribute:attribute
652                   withValue:value fromRow:row];
653     
654     if (first) first = NO;
655     else [expression appendString:@", "];
656
657     [expression appendString:value];
658   }
659     
660   return expression;
661 }
662
663 - (NSArray *)relationshipPathsForAttributes:(NSArray *)attributes
664   qualifier:(EOSQLQualifier *)qualifier
665   fetchOrder:(NSArray *)fetchOrder
666 {
667     int          i, count;
668     NSMutableSet *entities;
669     NSMutableSet *relationshipPaths;
670     NSEnumerator *enumerator;
671     id           entityOrRelationship;
672
673     entities          = [NSMutableSet set];
674     relationshipPaths = [NSMutableSet set];
675     
676     NSAssert3(self->entity,
677               @"entity should be non nil (attrs=%@, qual=%@, order=%@)",
678               attributes, qualifier, fetchOrder);
679     
680     for (i = 0, count = [attributes count]; i < count; i++) {
681       EOAttribute *attribute;
682
683       attribute = [attributes objectAtIndex:i];
684         
685       if ([attribute entity] != entity) {
686         [[[InvalidAttributeException alloc]
687                     initWithFormat:@"all attributes must be from the same "
688                         @"entity (attribute '%@' is not in '%@')",
689                         [attribute name],
690                         [entity name]] raise];
691       }
692
693       /* attribute is normal. */
694       [entities addObject:[attribute entity]];
695     }
696     
697     [relationshipPaths unionSet:[qualifier relationshipPaths]];
698     [entities unionSet:[qualifier additionalEntities]];
699     
700     for (i = 0, count = [fetchOrder count]; i < count; i++) {
701       EOAttribute *attribute;
702       id eorder;
703
704       eorder = [fetchOrder objectAtIndex:i];
705
706       attribute = ([eorder isKindOfClass:[EOSortOrdering class]])
707         ? [self->entity attributeNamed:[(EOSortOrdering *)eorder key]]
708         : [(EOAttributeOrdering *)eorder attribute];
709       
710       if ([attribute entity] != entity) {
711             [[[InvalidAttributeException alloc]
712                     initWithFormat:@"all attributes must be from the same "
713                         @"entity (attribute '%@' is not in '%@')",
714                         [attribute name],
715                         [entity name]] raise];
716       }
717     }
718
719     entitiesAndPropertiesAliases = [NSMutableDictionary new];
720     fromListEntities = [NSMutableArray new];
721     enumerator = [entities objectEnumerator];
722     i = 1;
723     while ((entityOrRelationship = [enumerator nextObject])) {
724         NSString* alias = [NSString stringWithFormat:@"t%d", i++];
725         
726         [entitiesAndPropertiesAliases setObject:alias
727                                       forKey:entityOrRelationship];
728         [fromListEntities addObject:entityOrRelationship];
729     }
730
731     return [relationshipPaths allObjects];
732 }
733
734 - (EOEntity *)entity {
735   return self->entity;
736 }
737 - (id)finishBuildingExpression {
738   return self;
739 }
740 - (NSString *)lockClause {
741   return @"";
742 }
743
744 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute
745   context:(id)context
746 {
747     NSString *alias      = [entitiesAndPropertiesAliases objectForKey:context];
748     NSString *columnName = nil;
749
750     columnName = adaptor
751         ? [adaptor formatAttribute:attribute]
752         : [attribute columnName];
753
754     return alias
755         ? [NSString stringWithFormat:@"%@.%@", alias, columnName]
756         : columnName;
757 }
758
759 - (NSString *)expressionValueForAttribute:(EOAttribute *)attribute {
760   /* attribute is a normal attribute. Its alias is the alias
761      of its entity. */
762   return [self expressionValueForAttribute:attribute
763                context:[attribute entity]];
764 }
765
766 - (NSString *)expressionValueForAttributePath:(NSArray *)definitionArray {
767     /* Take the alias of the last relationship. */
768   id       relationship;
769   NSString *alias;
770
771   relationship
772         = [definitionArray objectAtIndex:([definitionArray count] - 2)];
773   alias = [entitiesAndPropertiesAliases objectForKey:relationship];
774   
775   return AUTORELEASE(([[NSString alloc]
776                            initWithFormat:@"%@.%@",
777                            alias, [[definitionArray lastObject] columnName]]));
778 }
779
780 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)context {
781     return self->content;
782 }
783
784 + (Class)selectExpressionClass {
785   return [EOSelectSQLExpression class];
786 }
787 + (Class)insertExpressionClass {
788   return [EOInsertSQLExpression class];
789 }
790 + (Class)deleteExpressionClass {
791   return [EODeleteSQLExpression class];
792 }
793 + (Class)updateExpressionClass {
794   return [EOUpdateSQLExpression class];
795 }
796
797 - (EOAdaptor *)adaptor {
798   return self->adaptor;
799 }
800
801 /* description */
802
803 - (NSString *)description {
804   NSMutableString *ms;
805
806   ms = [NSMutableString stringWithCapacity:128];
807   [ms appendFormat:@"<0x%08X[%@]:\n", self, NSStringFromClass([self class])];
808   
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];
812   
813   if (self->entitiesAndPropertiesAliases) 
814     [ms appendFormat:@"  aliases=%@\n", self->entitiesAndPropertiesAliases];
815   if (self->fromListEntities)
816     [ms appendFormat:@"  from-entities=%@\n", self->fromListEntities];
817
818   if (self->whereClauseString)
819     [ms appendFormat:@"  where=%@\n", self->whereClauseString];
820   
821   if (self->listString) [ms appendFormat:@"  list=%@\n",     self->listString];
822   if (self->bindings)   [ms appendFormat:@"  bindings=%@\n", self->bindings];
823   
824   [ms appendString:@">"];
825   return ms;
826 }
827
828 @end /* EOSQLExpression */
829
830
831 @implementation EOInsertSQLExpression
832 @end /* EOInsertSQLExpression */
833
834 @implementation EOUpdateSQLExpression
835 @end /* EOUpdateSQLExpression */
836
837 @implementation EODeleteSQLExpression
838 @end /* EODeleteSQLExpression */
839
840 @implementation EOSQLExpression(NewInEOF2)
841
842 + (EOSQLExpression *)selectStatementForAttributes:(NSArray *)_attributes
843   lock:(BOOL)_flag
844   fetchSpecification:(EOFetchSpecification *)_fspec
845   entity:(EOEntity *)_entity
846 {
847     if (_fspec == nil) {
848       [NSException raise:NSInvalidArgumentException
849                    format:@"missing fetch specification argument .."];
850     }
851     if ([_attributes count] == 0) {
852       [NSException raise:NSInvalidArgumentException
853                    format:@"missing attributes for select .."];
854     }
855
856     return [self selectExpressionForAttributes:_attributes
857                  lock:_flag
858                  qualifier:[[_fspec qualifier] sqlQualifierForEntity:_entity]
859                  fetchOrder:[_fspec sortOrderings]
860                  channel:nil];
861 }
862
863 + (EOSQLExpression *)expressionForString:(NSString *)_sql {
864   EOSQLExpression *se;
865
866   se = [[EOSQLExpression alloc] init];
867   [se setStatement:_sql];
868   return AUTORELEASE(se);
869 }
870
871 /* accessors */
872
873 - (void)setStatement:(NSString *)_stmt {
874   id tmp;
875   tmp = self->content;
876   self->content = [_stmt mutableCopy];
877   RELEASE(tmp);
878 }
879 - (NSString *)statement {
880   return self->content;
881 }
882
883 - (NSString *)whereClauseString {
884   return self->whereClauseString;
885 }
886
887 /* tables */
888
889 - (NSString *)tableListWithRootEntity:(EOEntity *)_entity {
890   return ([self->fromListEntities count] > 0)
891     ? [self fromClause]
892     : [_entity externalName];
893 }
894
895 /* assembly */
896
897 - (NSString *)assembleDeleteStatementWithQualifier:(EOQualifier *)_qualifier
898   tableList:(NSString *)_tableList
899   whereClause:(NSString *)_whereClause
900 {
901   NSMutableString *s;
902   
903   s = [NSMutableString stringWithCapacity:64];
904   [s appendString:@"DELETE FROM "];
905   [s appendString:_tableList];
906   [s appendString:@" WHERE "];
907   [s appendString:_whereClause];
908   return s;
909 }
910
911 - (NSString *)assembleInsertStatementWithRow:(NSDictionary *)_row
912   tableList:(NSString *)_tables
913   columnList:(NSString *)_columns
914   valueList:(NSString *)_values
915 {
916   NSMutableString *s;
917   
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:@")"];
926   return s;
927 }
928
929 - (NSString *)assembleSelectStatementWithAttributes:(NSArray *)_attributes
930   lock:(BOOL)_lock
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
940 {
941   NSMutableString *s;
942   unsigned wlen, jlen;
943
944 #if 0
945 #warning DEBUG LOG, REMOVE!
946   [self logWithFormat:@"%s: '%@': %@", __PRETTY_FUNCTION__, _whereClause,self];
947 #endif
948   
949   s = [NSMutableString stringWithCapacity:256];
950   
951   [s appendString:_selectString ? _selectString : @"SELECT"];
952   [s appendString:@" "];
953   [s appendString:_columns];
954   [s appendString:@" FROM "];
955   [s appendString:_tables];
956   
957   if ([_lockClause length] > 0) {
958     [s appendString:@" "];
959     [s appendString:_lockClause];
960   }
961   
962   wlen = [_whereClause length];
963   jlen = [_joinClause  length];
964
965   if ((wlen > 0) || (jlen > 0))
966     [s appendString:@" WHERE "];
967   
968   if (wlen > 0)
969     [s appendString:_whereClause];
970
971   if ((wlen > 0) && (jlen > 0))
972     [s appendString:@" AND "];
973
974   if (jlen > 0)
975     [s appendString:_joinClause];
976   
977   if ([_orderByClause length] > 0) {
978     [s appendString:@" ORDER BY "];
979     [s appendString:_orderByClause];
980   }
981   
982   return s;
983 }
984
985 - (NSString *)assembleUpdateStatementWithRow:(NSDictionary *)_row
986   qualifier:(EOQualifier *)_qualifier
987   tableList:(NSString *)_tables
988   updateList:(NSString *)_updates
989   whereClause:(NSString *)_whereClause
990 {
991   NSMutableString *s;
992
993   s = [NSMutableString stringWithCapacity:256];
994
995   [s appendString:@"UPDATE "];
996   [s appendString:_tables];
997   [s appendString:@" SET "];
998   [s appendString:_updates];
999   [s appendString:@" WHERE "];
1000   [s appendString:_whereClause];
1001   
1002   return s;
1003 }
1004
1005 - (NSString *)assembleJoinClauseWithLeftName:(NSString *)_leftName
1006   rightName:(NSString *)_rightName
1007   joinSemantic:(EOJoinSemantic)_semantic
1008 {
1009   NSMutableString *s;
1010   
1011   s = [NSMutableString stringWithCapacity:64];
1012   [s appendString:_leftName];
1013   switch (_semantic) {
1014     case EOInnerJoin:
1015       [s appendString:@" = "];
1016       break;
1017     case EOFullOuterJoin:
1018       [s appendString:@" *=* "];
1019       break;
1020     case EOLeftOuterJoin:
1021       [s appendString:@" *= "];
1022       break;
1023     case EORightOuterJoin:
1024       [s appendString:@" =* "];
1025       break;
1026   }
1027   [s appendString:_rightName];
1028   return s;
1029 }
1030
1031 /* attributes */
1032
1033 - (NSString *)sqlStringForAttribute:(EOAttribute *)_attribute {
1034   NSLog(@"ERROR(%s): subclasses need to override this method!",
1035         __PRETTY_FUNCTION__);
1036   return nil;
1037 }
1038
1039 - (NSString *)sqlStringForAttributePath:(NSString *)_attrPath {
1040   NSLog(@"ERROR(%s): subclasses need to override this method!",
1041         __PRETTY_FUNCTION__);
1042   return nil;
1043 }
1044
1045 - (NSString *)sqlStringForAttributeNamed:(NSString *)_attrName {
1046   EOAttribute *a;
1047
1048   if ((a = [[self entity] attributeNamed:_attrName]))
1049     return [self sqlStringForAttribute:a];
1050   
1051   return [self sqlStringForAttributePath:_attrName];
1052 }
1053
1054 /* bind variables */
1055
1056 + (BOOL)useBindVariables {
1057   return NO;
1058 }
1059 - (BOOL)mustUseBindVariableForAttribute:(EOAttribute *)_attr {
1060   return NO;
1061 }
1062 - (BOOL)shouldUseBindVariableForAttribute:(EOAttribute *)_attr {
1063   return NO;
1064 }
1065
1066 - (NSMutableDictionary *)bindVariableDictionaryForAttribute:(EOAttribute *)_attr
1067   value:(id)_value
1068 {
1069   NSMutableDictionary *d;
1070   
1071   d = [NSMutableDictionary dictionaryWithCapacity:8];
1072   [d setObject:_attr                  forKey:EOBindVariableAttributeKey];
1073   [d setObject:_value ? _value : null forKey:EOBindVariableValueKey];
1074   return d;
1075 }
1076
1077 - (void)addBindVariableDictionary:(NSMutableDictionary *)_dictionary {
1078   if (self->bindings == nil)
1079     self->bindings = [[NSMutableArray alloc] init];
1080 }
1081 - (NSArray *)bindVariableDictionaries {
1082   return self->bindings;
1083 }
1084
1085 /* values */
1086
1087 + (NSString *)formatValue:(id)_value forAttribute:(EOAttribute *)_attribute {
1088   return _value ? _value : null;
1089 }
1090
1091 - (NSString *)sqlStringForValue:(id)_value attributeNamed:(NSString *)_attrName {
1092   NSMutableDictionary *bindVars;
1093   EOAttribute *attribute;
1094
1095   attribute = [[self entity] attributeNamed:_attrName];
1096
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];
1102   }
1103   else
1104     bindVars = nil;
1105
1106   if (bindVars) {
1107     [self addBindVariableDictionary:bindVars];
1108     return [bindVars objectForKey:EOBindVariablePlaceHolderKey];
1109   }
1110   
1111   return [[self class] formatValue:_value?_value:null forAttribute:attribute];
1112 }
1113
1114 + (NSString *)sqlPatternFromShellPattern:(NSString *)_pattern {
1115   unsigned len;
1116
1117   if ((len = [_pattern length]) > 0) {
1118     unsigned   cstrLen = [_pattern cStringLength];
1119     char       cstrBuf[cstrLen + 1];
1120     const char *cstr;
1121     char       buf[len * 3 + 1];
1122     unsigned   i;
1123     BOOL       didSomething = NO;
1124
1125     [_pattern getCString:cstrBuf];
1126     cstr = cstrBuf;
1127     
1128     for (i = 0; *cstr; cstr++) {
1129       switch (*cstr) {
1130         case '*':
1131           buf[i] = '%'; i++; didSomething = YES;
1132           break;
1133         case '?':
1134           buf[i] = '_'; i++; didSomething = YES;
1135           break;
1136           
1137         case '%':
1138           buf[i] = '['; i++;
1139           buf[i] = '%'; i++;
1140           buf[i] = ']'; i++;
1141           didSomething = YES;
1142           break;
1143           
1144         case '_':
1145           buf[i] = '['; i++;
1146           buf[i] = '_'; i++;
1147           buf[i] = ']'; i++;
1148           didSomething = YES;
1149           break;
1150           
1151         default:
1152           buf[i] = *cstr;
1153           i++;
1154           break;
1155       }
1156     }
1157     buf[i] = '\0';
1158     
1159     return (didSomething)
1160       ? [NSString stringWithCString:buf length:i]
1161       : _pattern;
1162   }
1163   return _pattern;
1164 }
1165
1166 /* SQL formats */
1167
1168 + (NSString *)formatSQLString:(NSString *)_sqlString format:(NSString *)_fmt {
1169   return _sqlString;
1170 }
1171
1172 /* qualifier operators */
1173
1174 - (NSString *)sqlStringForSelector:(SEL)_selector value:(id)_value {
1175   if ((_value == null) || (_value == nil)) {
1176     if (sel_eq(_selector, EOQualifierOperatorEqual))
1177       return @"is";
1178     else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1179       return @"is not";
1180   }
1181   else {
1182     if (sel_eq(_selector, EOQualifierOperatorEqual))
1183       return @"=";
1184     else if (sel_eq(_selector, EOQualifierOperatorNotEqual))
1185       return @"<>";
1186   }
1187   
1188   if (sel_eq(_selector, EOQualifierOperatorLessThan))
1189     return @"<";
1190   else if (sel_eq(_selector, EOQualifierOperatorGreaterThan))
1191     return @">";
1192   else if (sel_eq(_selector, EOQualifierOperatorLessThanOrEqualTo))
1193     return @"<=";
1194   else if (sel_eq(_selector, EOQualifierOperatorGreaterThanOrEqualTo))
1195     return @">=";
1196   else if (sel_eq(_selector, EOQualifierOperatorLike))
1197     return @"LIKE";
1198   else {
1199     return [NSString stringWithFormat:@"UNKNOWN<%@>",
1200                        NSStringFromSelector(_selector)];
1201   }
1202 }
1203
1204 /* qualifiers */
1205
1206 - (NSString *)sqlStringForKeyComparisonQualifier:(EOKeyComparisonQualifier *)_q {
1207   NSMutableString *s;
1208   NSString        *sql;
1209   EOAttribute     *a;
1210   
1211   s = [NSMutableString stringWithCapacity:64];
1212   
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];
1217   
1218   [s appendString:@" "];
1219   [s appendString:[self sqlStringForSelector:[_q selector] value:nil]];
1220   [s appendString:@" "];
1221   
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];
1226   
1227   return s;
1228 }
1229
1230 - (NSString *)sqlStringForKeyValueQualifier:(EOKeyValueQualifier *)_q {
1231   NSMutableString *s;
1232   NSString        *sql;
1233   EOAttribute     *a;
1234   id              v;
1235
1236   v = [_q value];
1237   s = [NSMutableString stringWithCapacity:64];
1238
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];
1243   
1244   [s appendString:@" "];
1245   sql = [self sqlStringForSelector:[_q selector] value:v];
1246   [s appendString:sql];
1247   [s appendString:@" "];
1248   
1249   if (([_q selector] == EOQualifierOperatorLike) ||
1250       ([_q selector] == EOQualifierOperatorCaseInsensitiveLike))
1251     v = [[self class] sqlPatternFromShellPattern:v];
1252   
1253   sql = [self sqlStringForValue:v attributeNamed:[_q key]];
1254   [s appendString:sql];
1255   return s;
1256 }
1257
1258 - (NSString *)sqlStringForNegatedQualifier:(EOQualifier *)_q {
1259   NSMutableString *s;
1260   NSString *sql;
1261
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:@")"];
1267   return s;
1268 }
1269
1270 - (NSString *)sqlStringForConjoinedQualifiers:(NSArray *)_qs {
1271   NSMutableString *s;
1272   unsigned i, count;
1273   id (*objAtIdx)(id,SEL,unsigned);
1274
1275   objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1276   for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1277     id<EOQualifierSQLGeneration> q;
1278     
1279     q = objAtIdx(self, @selector(objectAtIndex:), i);
1280
1281     if (s == nil)
1282       s = [NSMutableString stringWithCapacity:128];
1283     else
1284       [s appendString:@" AND "];
1285     
1286     [s appendString:[q sqlStringForSQLExpression:self]];
1287   }
1288   return s;
1289 }
1290
1291 - (NSString *)sqlStringForDisjoinedQualifiers:(NSArray *)_qs {
1292   NSMutableString *s;
1293   unsigned i, count;
1294   id (*objAtIdx)(id,SEL,unsigned);
1295
1296   objAtIdx = (void *)[_qs methodForSelector:@selector(objectAtIndex:)];
1297   for (i = 0, count = [_qs count], s = nil; i < count; i++) {
1298     id<EOQualifierSQLGeneration> q;
1299     
1300     q = objAtIdx(self, @selector(objectAtIndex:), i);
1301
1302     if (s == nil)
1303       s = [NSMutableString stringWithCapacity:128];
1304     else
1305       [s appendString:@" OR "];
1306     
1307     [s appendString:[q sqlStringForSQLExpression:self]];
1308   }
1309   return s;
1310 }
1311
1312 /* list strings */
1313
1314 - (NSMutableString *)listString {
1315   if (self->listString == nil)
1316     self->listString = [[NSMutableString alloc] initWithCapacity:128];
1317   return self->listString;
1318 }
1319
1320 - (void)appendItem:(NSString *)_itemString toListString:(NSMutableString *)_ls {
1321   if ([_ls length] > 0)
1322     [_ls appendString:@","];
1323   [_ls appendString:_itemString];
1324 }
1325
1326 /* deletes */
1327
1328 - (void)prepareDeleteExpressionForQualifier:(EOQualifier *)_qual {
1329   NSString *tableList, *sql;
1330   
1331   self->whereClauseString =
1332     [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1333
1334   tableList = [self tableListWithRootEntity:[self entity]];
1335   
1336   sql = [self assembleDeleteStatementWithQualifier:_qual
1337               tableList:tableList
1338               whereClause:[self whereClauseString]];
1339   
1340   [self setStatement:sql];
1341 }
1342
1343 /* updates */
1344
1345 - (void)addUpdateListAttribute:(EOAttribute *)_attr value:(NSString *)_value {
1346   NSMutableString *s;
1347
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];
1353   
1354   [self appendItem:s toListString:[self listString]];
1355   RELEASE(s);
1356 }
1357
1358 - (void)prepareUpdateExpressionWithRow:(NSDictionary *)_row
1359   qualifier:(EOQualifier *)_qual
1360 {
1361   NSEnumerator *keys;
1362   NSString     *key;
1363   NSString     *tableList, *sql;
1364   
1365   keys = [_row keyEnumerator];
1366   while ((key = [keys nextObject])) {
1367     EOAttribute *attribute;
1368     id          value;
1369     
1370     attribute = [self->entity attributeNamed:key];
1371     value     = [_row objectForKey:key];
1372     
1373     [self addUpdateListAttribute:attribute value:value];
1374   }
1375   
1376   self->whereClauseString =
1377     [[(id<EOQualifierSQLGeneration>)_qual sqlStringForSQLExpression:self] copy];
1378
1379   tableList = [self tableListWithRootEntity:[self entity]];
1380
1381   sql = [self assembleUpdateStatementWithRow:_row
1382               qualifier:_qual
1383               tableList:tableList
1384               updateList:[self listString]
1385               whereClause:[self whereClauseString]];
1386   
1387   [self setStatement:sql];
1388 }
1389
1390 @end /* EOSQLExpression(NewInEOF2) */