]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOSQLQualifier.m
added missing inline pathes
[sope] / sope-gdl1 / GDLAccess / EOSQLQualifier.m
1 /* 
2    EOSQLQualifier.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@apache.org>
7            Helge Hess <helge.hess@opengroupware.org>
8    Date:   September 1996
9            November  1999
10
11    This file is part of the GNUstep Database Library.
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Library General Public
15    License as published by the Free Software Foundation; either
16    version 2 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Library General Public License for more details.
22
23    You should have received a copy of the GNU Library General Public
24    License along with this library; see the file COPYING.LIB.
25    If not, write to the Free Software Foundation,
26    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 #include <stdio.h>
30 #import "common.h"
31 #import "EOSQLQualifier.h"
32 #import "EOAdaptor.h"
33 #import "EOAttribute.h"
34 #import "EOEntity.h"
35 #import "EOExpressionArray.h"
36 #import "EOFExceptions.h"
37 #import "EORelationship.h"
38 #import "EOSQLExpression.h"
39 #include <EOControl/EOKeyValueCoding.h>
40 #include <EOControl/EONull.h>
41 #import "EOQualifierScanner.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 @interface EOQualifierJoinHolder : NSObject
52 {
53   id source;
54   id destination;
55 }
56 + (id)valueForSource:(id)source destination:(id)destination;
57 - (NSString*)expressionValueForContext:(EOSQLExpression*)context;
58 - (id)source;
59 - (id)destination;
60 @end
61
62 @implementation EOQualifierJoinHolder
63
64 static Class  AttributeClass = Nil;
65 static EONull *null          = nil;
66
67 + (void)initialize {
68   AttributeClass = [EOAttribute class];
69   null           = [[NSNull null] retain];
70 }
71
72 + (id)valueForSource:(id)_source destination:(id)_destination {
73   EOQualifierJoinHolder *value;
74   
75   value              = [[[self alloc] init] autorelease];
76   value->source      = [_source      retain];
77   value->destination = [_destination retain];
78   return value;
79 }
80
81 - (NSString *)expressionValueForContext:(EOSQLExpression *)context {
82   NSMutableString *result         = nil;
83   EOAdaptor       *adaptor        = nil;
84   NSString        *formattedLeft  = nil;
85   NSString        *formattedRight = nil;
86   BOOL            checkNull       = NO;
87
88   adaptor = [context adaptor];
89   
90   if ([source isKindOfClass:AttributeClass]) {
91     formattedLeft = [(EOAttribute *)source expressionValueForContext:context];
92   }
93   else {
94     NSAssert([destination isKindOfClass:AttributeClass],
95              @"either one of source or destination should be EOAttribute");
96     if ([source isEqual:null] || (source == nil))
97       checkNull = YES;
98     
99     formattedLeft = [adaptor formatValue:source?source:null
100                              forAttribute:destination];
101   }
102
103   if ([destination isKindOfClass:AttributeClass]) {
104     NSString *tmp = formattedLeft;
105     
106     formattedLeft  = 
107       [(EOAttribute *)destination expressionValueForContext:context];
108     formattedRight = tmp;
109   }
110   else {
111     NSAssert([source isKindOfClass:AttributeClass],
112              @"either one of source or destination should be EOAttribute");
113     
114     if ([destination isEqual:null] || (destination == nil))
115       checkNull = YES;
116     
117     formattedRight = [adaptor formatValue:destination?destination:null
118                               forAttribute:source];
119   }
120
121   result = [NSMutableString stringWithCapacity:64];
122   [result appendString:formattedLeft];
123   [result appendString:checkNull ? @" IS " : @"="];
124   [result appendString:formattedRight];
125   return result;
126 }
127
128 - (id)source {
129     return self->source;
130 }
131 - (id)destination {
132     return self->destination;
133 }
134
135 @end /* EOQualifierJoinHolder */
136
137
138 @implementation EOSQLQualifier
139
140 + (EOSQLQualifier*)qualifierForRow:(NSDictionary*)row
141   entity:(EOEntity*)_entity
142 {
143   EOSQLQualifier  *qualifier     = nil;
144   NSEnumerator    *enumerator    = nil;
145   NSString        *attributeName = nil;
146   EOAttribute     *attribute     = nil;
147   id              value          = nil;
148   BOOL            first          = YES;
149
150   enumerator = [row keyEnumerator];    
151   qualifier  = [[[EOSQLQualifier alloc] init] autorelease];
152
153   while ((attributeName = [enumerator nextObject])) {
154     attribute = [_entity attributeNamed:attributeName];
155     value = [row objectForKey:attributeName];
156
157     if ((value == nil) || (attribute == nil))
158       /* return nil when is unable to build a qualifier for all keys
159          in the given row
160       */
161       return nil;
162
163     if (first)
164       first = NO;
165     else
166       [qualifier->content addObject:@" AND "];
167
168     [qualifier->content addObject:
169               [EOQualifierJoinHolder valueForSource:attribute destination:value]];
170   }
171
172   qualifier->entity = RETAIN(_entity);
173   [qualifier _computeRelationshipPaths];
174
175   return qualifier;
176 }
177
178 + (EOSQLQualifier*)qualifierForPrimaryKey:(NSDictionary*)dictionary
179   entity:(EOEntity*)_entity
180 {
181   NSDictionary *pkey = nil;
182
183   pkey = [_entity primaryKeyForRow:dictionary];
184     /* return nil when is unable to build a complete qualifier
185        for all primary key attributes
186     */
187   return pkey ? [self qualifierForRow:pkey entity:_entity] : nil;
188 }
189
190 + (EOSQLQualifier*)qualifierForRow:(NSDictionary*)row 
191   relationship:(EORelationship*)relationship
192 {
193   NSArray        *componentRelationships = nil;
194   EOSQLQualifier *qualifier              = nil;
195   NSArray        *sourceAttributes       = nil;
196   id             tmpRelationship         = nil;
197   EOAttribute    *sourceAttribute        = nil;
198   EOAttribute    *destinationAttribute   = nil;
199   id             value                   = nil;
200   int            j                       = 0;
201   int            count2                  = 0;
202
203   componentRelationships = [relationship componentRelationships];
204   tmpRelationship        = relationship;
205   qualifier              = [[[EOSQLQualifier alloc] init] autorelease];
206
207   /* Make a qualifier string in the following manner. If the relationship is
208      not flattened we must join using the join operator the values from `row'
209      and the foreign keys taken from the destination entity of relatioship.
210      If the relationship is flattend we must append then joins between the
211      components of relationship. */
212
213   if (componentRelationships) {
214     tmpRelationship = [componentRelationships objectAtIndex:0];
215         
216     sourceAttributes =
217       [NSArray arrayWithObject:[tmpRelationship sourceAttribute]];
218   }
219   else {
220     sourceAttributes =
221       [NSArray arrayWithObject:[relationship sourceAttribute]];
222   }
223
224   sourceAttribute = [tmpRelationship sourceAttribute];
225   value           = [row objectForKey:[sourceAttribute name]];
226   if (value == nil)
227     /* Returns nil if `row' does not contain all the values needed to 
228        create a complete qualifier
229     */
230     return nil;
231
232   destinationAttribute = [tmpRelationship destinationAttribute];
233   [qualifier->content addObject:
234             [EOQualifierJoinHolder valueForSource:destinationAttribute
235                                    destination:value]];
236
237   if (componentRelationships) {
238     EOEntity *tempEntity = [tmpRelationship destinationEntity];
239
240     /* The relationship is flattened. Iterate over the components and 
241        add joins that `link' the components between them.
242     */
243     count2 = [componentRelationships count];
244     for (j = 1; j < count2; j++) {
245       relationship = [componentRelationships objectAtIndex:j];
246             
247       if ([relationship sourceAttribute]) {
248         [qualifier->content addObject:@" AND "];
249         [qualifier->content addObject:
250                   [EOQualifierJoinHolder valueForSource:
251                                          [relationship sourceAttribute]
252                                          destination:
253                                          [relationship destinationAttribute]]];
254       }
255     }
256
257     /* Here we make a hack because later we need to use this qualifier in
258        a SELECT expression in which the qualifier's entity should be the
259        final destination entity of the flattened relationship. In addition
260        we need in the FROM clause all the entities corresponding to the
261        components of the relationship to be able to insert the joins
262        between the values given in row and the final attributes from the
263        destination entity of the last component of relationship. */
264     ASSIGN(qualifier->entity, tempEntity);
265     [qualifier _computeRelationshipPaths];
266     ASSIGN(qualifier->entity, [relationship destinationEntity]);
267     return qualifier;
268   }
269   else {
270     ASSIGN(qualifier->entity, [relationship destinationEntity]);
271     return qualifier;
272   }
273 }
274
275 + (EOSQLQualifier *)qualifierForObject:sourceObject 
276   relationship:(EORelationship *)relationship
277 {
278   return [self qualifierForRow:
279                  [sourceObject valueForKey:[[relationship sourceAttribute] name]]
280                relationship:relationship];
281 }
282
283 - (id)init {
284   NSZone *z = [self zone];
285     
286   RELEASE(self->content);            self->content            = nil;
287   RELEASE(self->relationshipPaths);  self->relationshipPaths  = nil;
288   RELEASE(self->additionalEntities); self->additionalEntities = nil;
289
290   self->content            = [[EOExpressionArray allocWithZone:z] init];
291   self->relationshipPaths  = [[NSMutableSet allocWithZone:z] init];
292   self->additionalEntities = [[NSMutableSet allocWithZone:z] init];
293   return self;
294 }
295
296 - (id)initWithEntity:(EOEntity *)_entity 
297   qualifierFormat:(NSString *)_qualifierFormat
298   argumentsArray:(NSArray *)_args
299 {
300   PrintfFormatScanner           *formatScanner       = nil;
301   EOQualifierEnumScannerHandler *scannerHandler      = nil;
302   NSString                      *qualifierString     = nil;
303   NSMutableArray                *myRelationshipPaths = nil;
304   NSEnumerator                  *args                = nil;
305
306   myRelationshipPaths = [[NSMutableArray allocWithZone:[self zone]] init];
307     
308   [self init];
309   ASSIGN(self->entity, _entity);
310
311   if (_qualifierFormat == nil)
312     return self;
313
314   formatScanner  = [[PrintfFormatScanner alloc] init];
315   scannerHandler = [[EOQualifierEnumScannerHandler alloc] init];
316   [formatScanner setAllowOnlySpecifier:YES];
317
318   args = [_args objectEnumerator];
319   [scannerHandler setEntity:_entity];
320     
321   [formatScanner setFormatScannerHandler:scannerHandler];
322   /*
323     Note: This is an ugly hack. Arguments is supposed to be a va_args
324           structure, but an NSArray is passed in.
325           It works because the value is casted to -parseFormatString:context:
326           which gives control to the scannerHandler which casts the va_args
327           back to an array (the EOQualifierEnumScannerHandler does that).
328           Works on ix86, but *NOT* on iSeries or zServer !!
329   */
330 #if defined(__s390__)
331   qualifierString =
332     [formatScanner performSelector:@selector(stringWithFormat:arguments:)
333                    withObject:_qualifierFormat
334                    withObject:args];
335 #else
336   // TODO: args is an NSArray, PrintfFormatScanner expects a va_list?
337   //       I think that this is OK because we use EOQualifierEnumScannerHandler
338   qualifierString = 
339     [formatScanner stringWithFormat:_qualifierFormat
340                    arguments:(void *)args];
341 #endif
342
343   [formatScanner  release]; formatScanner  = nil;
344   [scannerHandler release]; scannerHandler = nil;
345   
346   [self->content release]; self->content = nil;
347   self->content =
348          [[EOExpressionArray parseExpression:qualifierString
349                             entity:entity
350                             replacePropertyReferences:YES
351                             relationshipPaths:myRelationshipPaths]
352            retain];
353   [self _computeRelationshipPaths:myRelationshipPaths];
354   [myRelationshipPaths release]; myRelationshipPaths = nil;
355   return self;
356 }
357
358 - (id)initWithEntity:(EOEntity*)_entity 
359   qualifierFormat:(NSString *)qualifierFormat, ...
360 {
361   va_list        ap;
362   id             formatScanner        = nil;
363   id             scannerHandler       = nil;
364   NSString       *qualifierString     = nil;
365   NSMutableArray *myRelationshipPaths = nil;
366
367   if ((self = [self init]) == nil)
368     return nil;
369   
370   myRelationshipPaths = [[NSMutableArray alloc] init];
371   ASSIGN(self->entity, _entity);
372   
373   if (qualifierFormat == nil) {
374     return self;
375   }
376   
377   formatScanner  = [[PrintfFormatScanner alloc] init];
378   scannerHandler = [[EOQualifierScannerHandler alloc] init];
379   [formatScanner setAllowOnlySpecifier:YES];
380   
381   va_start(ap, qualifierFormat);
382   [scannerHandler setEntity:_entity];
383   [formatScanner setFormatScannerHandler:scannerHandler];
384   qualifierString = [formatScanner stringWithFormat:qualifierFormat
385                                    arguments:ap];
386   va_end(ap);
387
388   [formatScanner  release];
389   [scannerHandler release];
390
391   [self->content release]; self->content = nil;
392   self->content =
393          [[EOExpressionArray parseExpression:qualifierString
394                             entity:entity
395                             replacePropertyReferences:YES
396                             relationshipPaths:myRelationshipPaths] retain];
397   [self _computeRelationshipPaths:myRelationshipPaths];
398   [myRelationshipPaths release]; myRelationshipPaths = nil;
399   return self;
400 }
401
402 - (void)_computeRelationshipPaths {
403   [self _computeRelationshipPaths:nil];
404 }
405
406 static void
407 handle_attribute(EOSQLQualifier *self, id object, id _relationshipPaths)
408 {
409   [self->additionalEntities addObject:[object entity]];
410 }
411
412 - (void)_computeRelationshipPaths:(NSArray *)_relationshipPaths {
413   int i, count;
414
415   [relationshipPaths removeAllObjects];
416
417   if (_relationshipPaths) {
418     NSEnumerator *pathEnum = [_relationshipPaths objectEnumerator];
419     NSArray      *relPath  = nil;
420         
421     while ((relPath = [pathEnum nextObject])) {
422       NSEnumerator   *relEnum = nil;
423       EORelationship *rel     = nil;
424
425       relEnum = [relPath objectEnumerator];
426
427       while ((rel = [relEnum nextObject])) {
428         [additionalEntities addObject:[rel destinationEntity]];
429       }
430     }
431     [relationshipPaths addObjectsFromArray:_relationshipPaths];
432   }
433   for (i = 0, count = [content count]; i < count; i++) {
434     id object = [content objectAtIndex:i];
435
436     /* The objects from content can only be NSString, values or
437        EOAttribute. */
438     if ([object isKindOfClass:[EOAttribute class]]) {
439       handle_attribute (self, object, _relationshipPaths);
440     }
441     else if ([object isKindOfClass:[EOQualifierJoinHolder class]]) {
442       id source      = nil;
443       id destination = nil;
444
445       source      = [object source];
446       destination = [object destination];
447
448       if ([source isKindOfClass:[EOAttribute class]])
449         handle_attribute (self, source, _relationshipPaths);
450       if ([destination isKindOfClass:[EOAttribute class]])
451         handle_attribute (self, destination, _relationshipPaths);
452     }
453     else if ([object isKindOfClass:[EORelationship class]]) {
454       [[[InvalidPropertyException alloc]
455                                        initWithFormat:@"cannot refer a EORelat"
456                                        @"ionship in a EOSQLQualifier: '%@'",
457                                        [(EORelationship*)object name]] raise];
458     }
459   }
460 }
461
462 - (void)dealloc {
463   RELEASE(self->relationshipPaths);
464   RELEASE(self->additionalEntities);
465   
466   RELEASE(self->entity);
467   RELEASE(self->content);
468   [super dealloc];
469 }
470
471 - (id)copy {
472   return [self copyWithZone:NSDefaultMallocZone()];
473 }
474
475 - (id)copyWithZone:(NSZone*)zone {
476   EOSQLQualifier* copy = nil;
477
478   copy                    = [[self->isa allocWithZone:zone] init];
479   copy->entity            = RETAIN(self->entity);
480   copy->content           = [self->content           mutableCopyWithZone:zone];
481   copy->relationshipPaths = [self->relationshipPaths mutableCopyWithZone:zone];
482   copy->usesDistinct      = self->usesDistinct;
483   return copy;
484 }
485
486 - (void)negate {
487   [self->content insertObject:@"NOT (" atIndex:0];
488   [self->content addObject:@")"];
489 }
490
491 - (void)conjoinWithQualifier:(EOSQLQualifier*)qualifier {
492   if (![qualifier isKindOfClass:[EOSQLQualifier class]]) {
493     [NSException raise:NSInvalidArgumentException
494                  format:@"argument of conjoinWithQualifier: method must "
495                      @"be EOSQLQualifier"];
496   }
497
498   if (self->entity != qualifier->entity) {
499     [NSException raise:NSInvalidArgumentException
500                  format:@"qualifier argument of conjoinWithQualifier: "
501                              @"must have the same entity as receiver"];
502   }
503
504   [self->content insertObject:@"(" atIndex:0];
505   [self->content addObject:@") AND ("];
506   [self->content addObjectsFromExpressionArray:qualifier->content];
507   [self->content addObject:@")"];
508   [self->relationshipPaths unionSet:qualifier->relationshipPaths];
509 }
510
511 - (void)disjoinWithQualifier:(EOSQLQualifier*)qualifier {
512   if (![qualifier isKindOfClass:[EOSQLQualifier class]]) {
513     [NSException raise:NSInvalidArgumentException
514                  format:@"argument of disjoinWithQualifier: method must "
515                     @"be EOSQLQualifier"];
516   }
517
518   if (self->entity != qualifier->entity) {
519     [NSException raise:NSInvalidArgumentException
520                  format:@"qualifier argument of disjoinWithQualifier: "
521                     @"must have the same entity as receiver"];
522   }
523
524   [self->content insertObject:@"(" atIndex:0];
525   [self->content addObject:@") OR ("];
526   [self->content addObjectsFromExpressionArray:qualifier->content];
527   [self->content addObject:@")"];
528   [self->relationshipPaths unionSet:qualifier->relationshipPaths];
529 }
530
531 - (EOEntity*)entity {
532   return self->entity;
533 }
534 - (BOOL)isEmpty {
535   return (self->entity == nil) ? YES : NO;
536 }
537 - (void)setUsesDistinct:(BOOL)flag {
538   self->usesDistinct = flag;
539 }
540 - (BOOL)usesDistinct {
541   return self->usesDistinct;
542 }
543 - (NSMutableSet*)relationshipPaths {
544   return self->relationshipPaths;
545 }
546 - (NSMutableSet*)additionalEntities {
547   return self->additionalEntities;
548 }
549
550 - (NSString*)expressionValueForContext:(id<EOExpressionContext>)ctx {
551     return [self->content expressionValueForContext:ctx];
552 }
553
554 - (EOSQLQualifier *)sqlQualifierForEntity:(EOEntity *)_entity {
555   NSAssert3(self->entity == _entity,
556             @"passed invalid entity to %@ (contains %@, got %@)",
557             self, self->entity, _entity);
558   return (EOSQLQualifier *)self;
559 }
560
561 /* description */
562
563 - (NSString *)description {
564   NSMutableString *ms;
565
566   ms = [NSMutableString stringWithCapacity:128];
567   [ms appendFormat:@"<0x%08X[%@]:\n", self, NSStringFromClass([self class])];
568
569   [ms appendFormat:@" entity=%@", [self->entity name]];
570   
571   if (self->content)
572     [ms appendFormat:@" content=%@", self->content];
573   
574   if (self->usesDistinct)
575     [ms appendString:@" distinct"];
576   
577   [ms appendString:@">"];
578   return ms;
579 }
580
581 @end /* EOSQLQualifier */