]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EODatabase.m
renamed PostgreSQL72 to PostgreSQL, install in Library/GDLAdaptors-1.1
[sope] / sope-gdl1 / GDLAccess / EODatabase.m
1 /* 
2    EODatabase.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
7    Date: 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 #import "common.h"
28 #import "EODatabase.h"
29 #import "EOAdaptor.h"
30 #import "EOModel.h"
31 #import "EOEntity.h"
32 #import "EOGenericRecord.h"
33 #import "EODatabaseContext.h"
34 #import "EOObjectUniquer.h"
35 #import "EODatabaseFault.h"
36
37 NSTimeInterval NSDistantPastTimeInterval = 0.0;
38
39 @implementation EODatabase
40
41 // Database Global Methods
42
43 static NSMutableArray  *databaseInstances = nil;
44 static NSRecursiveLock *lock              = nil;
45
46 + (void)initialize {
47   static BOOL isInitialized = NO;
48   // THREAD
49   if (!isInitialized) {
50     isInitialized = YES;
51     databaseInstances = [[NSMutableArray alloc] init];
52     lock              = [[NSRecursiveLock alloc] init];
53   }
54 }
55
56 static inline void _addDatabaseInstance(EODatabase *_db) {
57   [lock lock];
58   [databaseInstances addObject:[NSValue valueWithNonretainedObject:_db]];
59   [lock unlock];
60 }
61 static inline void _removeDatabaseInstance(EODatabase *_db) {
62   [lock lock];
63   {
64     int i;
65     
66     for (i = [databaseInstances count] - 1; i >= 0; i--) {
67       EODatabase *db;
68
69       db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
70       if (db == _db) {
71         [databaseInstances removeObjectAtIndex:i];
72         break;
73       }
74     }
75   }
76   [lock unlock];
77 }
78
79 /*
80  * Initializing new instances
81  */
82
83 - (id)initWithAdaptor:(EOAdaptor *)_adaptor {
84   if (_adaptor == nil) {
85     AUTORELEASE(self);
86     return nil;
87   }
88     
89   self->adaptor           = RETAIN(_adaptor);
90   self->objectsDictionary = [[EOObjectUniquer allocWithZone:[self zone]] init];
91   self->contexts          = [[NSMutableArray  allocWithZone:[self zone]] init];
92     
93   self->flags.isUniquingObjects  = YES;
94   self->flags.isKeepingSnapshots = YES;
95   self->flags.isLoggingWarnings  = YES;
96
97   _addDatabaseInstance(self);
98     
99   return self;
100 }
101
102 - (id)initWithModel:(EOModel *)_model {
103   return [self initWithAdaptor:[EOAdaptor adaptorWithModel:_model]];
104 }
105 - (id)init {
106   return [self initWithAdaptor:nil];
107 }
108
109 - (void)dealloc {
110   _removeDatabaseInstance(self);
111   
112   RELEASE(self->adaptor);
113   RELEASE(self->objectsDictionary);
114   RELEASE(self->contexts);
115   [super dealloc];
116 }
117
118 // accessors
119
120 - (EOAdaptor *)adaptor {
121   return self->adaptor;
122 }
123
124 - (EOObjectUniquer *)objectUniquer {
125   return self->objectsDictionary;
126 }
127
128 // Checking connection status
129
130 - (BOOL)hasOpenChannels {
131   int i;
132     
133   for (i = ([self->contexts count] - 1); i >= 0; i--) {
134     if ([[[self->contexts objectAtIndex:i] nonretainedObjectValue]
135                           hasOpenChannels])
136       return YES;
137   }
138   return NO;
139 }
140
141 /*
142  * Getting the database contexts
143  */
144
145 - (id)createContext {
146   return AUTORELEASE([[EODatabaseContext alloc] initWithDatabase:self]);
147 }
148
149 - (NSArray *)contexts {
150   NSMutableArray *array = nil;
151   int i, n;
152
153   n = [self->contexts count];
154   array = [[NSMutableArray alloc] initWithCapacity:n];
155     
156   for (i = 0; i < n; i++) {
157     EODatabaseContext *ctx;
158
159     ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
160     [array addObject:ctx];
161   }
162   return AUTORELEASE(array);
163 }
164
165 - (void)contextDidInit:(id)_context {
166   [self->contexts addObject:[NSValue valueWithNonretainedObject:_context]];
167 }
168
169 - (void)contextWillDealloc:(id)aContext {
170   int i;
171     
172   for (i = [self->contexts count]-1; i >= 0; i--) {
173     if ([[self->contexts objectAtIndex:i] nonretainedObjectValue] == aContext) {
174       [self->contexts removeObjectAtIndex:i];
175       break;
176     }
177   }
178 }
179
180 /*
181  * Uniquing/snapshotting
182  */
183
184 - (void)setUniquesObjects:(BOOL)yn {
185   if ([self hasOpenChannels]) {
186     [NSException raise:NSInvalidArgumentException
187                  format:
188               @"EODatabase:%x: All channels must be closed when changing "
189               @"uniquing mode in the EODatabase, "
190               @"in [EODatabase setUniquesObjects:]",
191                  self];
192   }
193
194   if ((!yn) && (self->flags.isUniquingObjects))
195     [self->objectsDictionary forgetAllObjects];
196   self->flags.isUniquingObjects = yn;
197 }
198 - (BOOL)uniquesObjects {
199   return self->flags.isUniquingObjects;
200 }
201
202 - (void)setKeepsSnapshots:(BOOL)yn {
203   if ([self hasOpenChannels]) {
204     [NSException raise:NSInvalidArgumentException
205                  format:
206               @"EODatabase:%x: All channels must be closed when changing "
207               @"snapshoting mode in the EODatabase, "
208               @"in [EODatabase setKeepsSnapshots:]",
209                  self];
210   }
211
212   if ((yn == NO) && self->flags.isKeepingSnapshots)
213     [self->objectsDictionary forgetAllSnapshots];
214   
215   self->flags.isKeepingSnapshots = yn;
216 }
217 - (BOOL)keepsSnapshots {
218   return self->flags.isKeepingSnapshots;
219 }
220
221 // ******************** Handle Objects ********************
222
223 - (void)forgetAllObjects {
224   [self->objectsDictionary forgetAllObjects];
225 }
226
227 + (void)forgetObject:(id)_object {
228   [EOObjectUniquer forgetObject:_object];
229
230   [lock lock];
231   {
232     int i;
233     
234     for (i = [databaseInstances count] - 1; i >= 0; i--) {
235       EODatabase *db;
236
237       db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
238       [db forgetObject:_object];
239     }
240   }
241   [lock unlock];
242 }
243
244 - (void)forgetObject:(id)_object {
245   /*
246     NSLog(@"DB[0x%08X]: forget object 0x%08X<%s> entity=%@",
247         self, _object, class_get_class_name(*(Class *)_object),
248         [[_object entity] name]);
249   */
250   [self->objectsDictionary forgetObject:_object];
251 }
252
253 - (void)forgetAllSnapshots {
254   [self->objectsDictionary forgetAllSnapshots];
255 }
256
257 - (id)objectForPrimaryKey:(NSDictionary *)_key entity:(EOEntity *)_entity {
258   if (self->flags.isUniquingObjects && (_key != nil) && (_entity != nil)) {
259     _key = [_entity primaryKeyForRow:_key];
260     if (_key == nil)
261       return nil;
262     else {
263       id object = [self->objectsDictionary objectForPrimaryKey:_key entity:_entity];
264       
265 #if 0
266       if (object) {
267         if (![object isKindOfClass:[EOGenericRecord class]])
268           NSLog(@"object 0x%08X pkey=%@ entity=%@", object, _key, _entity);
269       }
270 #endif
271       return object;
272     }
273   }
274   return nil;
275 }
276
277 - (NSDictionary *)snapshotForObject:_object {
278   EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
279     
280   return rec ? rec->snapshot : nil;
281 }
282
283 - (NSDictionary *)primaryKeyForObject:(id)_object {
284   EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
285     
286   return rec ? rec->pkey : nil;
287 }
288
289 - (void)primaryKey:(NSDictionary**)_key
290   andSnapshot:(NSDictionary**)_snapshot
291   forObject:(id)_object {
292
293   EOUniquerRecord *rec = [self->objectsDictionary recordForObject:_object];
294     
295   if (rec) {
296     if (_key)      *_key      = rec->pkey;
297     if (_snapshot) *_snapshot = rec->snapshot;
298   }
299   else {
300     if (_key)      *_key      = nil;
301     if (_snapshot) *_snapshot = nil;
302   }
303 }
304
305 - (void)recordObject:(id)_object
306   primaryKey:(NSDictionary *)_key
307   snapshot:(NSDictionary *)_snapshot {
308
309   EOEntity *entity;
310
311   entity = [_object respondsToSelector:@selector(entity)]
312     ? [_object entity]
313     : [[self->adaptor model] entityForObject:_object];
314     
315   [self recordObject:_object
316         primaryKey:_key
317         entity:entity
318         snapshot:_snapshot];
319 }
320
321 - (void)recordObject:(id)_object
322   primaryKey:(NSDictionary *)_key
323   entity:(EOEntity *)_entity
324   snapshot:(NSDictionary *)_snapshot {
325
326   if (_object == nil) {
327     [NSException raise:NSInvalidArgumentException
328                  format:
329                    @"EODatabase:%x: Cannot record null object, "
330                    @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
331                    self];
332   }
333   if ((_entity == nil) && self->flags.isUniquingObjects) {
334     [NSException raise:NSInvalidArgumentException
335                  format:
336               @"EODatabase:%x: Cannot record object with null entity "
337               @"when the database is uniquing objects, "
338               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
339                  self];
340   }
341   _key = [_entity primaryKeyForRow:_key];
342   if ((_key == nil) && self->flags.isUniquingObjects) {
343     [NSException raise:NSInvalidArgumentException
344                  format:
345               @"EODatabase:%x: Cannot record object with null key "
346               @"when the database is uniquing objects, "
347               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
348                  self];
349   }
350   if ((_snapshot == nil) && self->flags.isKeepingSnapshots) {
351     [NSException raise:NSInvalidArgumentException
352                  format:
353               @"EODatabase:%x: Cannot record object with null snapshot "
354               @"when the database is keeping snapshots, "
355               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
356                  self];
357   }
358
359   [objectsDictionary recordObject:_object 
360                      primaryKey:self->flags.isUniquingObjects?_key:nil
361                      entity:self->flags.isUniquingObjects ?_entity:nil
362                      snapshot:self->flags.isKeepingSnapshots?_snapshot:nil];
363 }
364
365 - (BOOL)isObject:(id)_object 
366   updatedOutsideContext:(EODatabaseContext *)_context 
367 {
368   int i;
369     
370   for (i = [contexts count] - 1; i >= 0; i--) {
371     EODatabaseContext *ctx;
372
373     ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
374         
375     if ((ctx != _context) && [ctx isObjectUpdated:_object])
376       return YES;
377   }
378   return NO;
379 }
380
381 // ******************** Error messages ********************
382  
383 - (void)setLogsErrorMessages:(BOOL)yn {
384   self->flags.isLoggingWarnings = yn;
385 }
386 - (BOOL)logsErrorMessages {
387   return self->flags.isLoggingWarnings;
388 }
389
390 - (void)reportErrorFormat:(NSString*)format, ... {
391   va_list va;
392     
393   va_start(va, format);
394   [self reportErrorFormat:format arguments:va];
395   va_end(va);
396 }
397
398 - (void)reportErrorFormat:(NSString*)format arguments:(va_list)arguments {
399   [self reportError:AUTORELEASE([[NSString alloc] initWithFormat:format 
400                                                   arguments:arguments])];
401 }
402
403 - (void)reportError:(NSString*)error {
404   if (self->flags.isLoggingWarnings)
405     NSLog(@"EODatabase:%x:%@", self, error);
406 }
407
408 @end /* EODatabase */
409
410 @implementation EODatabase(EOF2Additions)
411
412 - (NSArray *)models {
413   EOModel *model;
414
415   model = [[self adaptor] model];
416   return model ? [NSArray arrayWithObject:model] : nil;
417 }
418
419 - (void)addModel:(EOModel *)_model {
420   EOModel *model;
421
422   model = [[self adaptor] model];
423
424   if (model == nil)
425     [[self adaptor] setModel:_model];
426   else
427     [self notImplemented:_cmd];
428 }
429
430 - (BOOL)addModelIfCompatible:(EOModel *)_model {
431   NSEnumerator *e;
432   EOModel *m;
433
434   if (![[self adaptor] canServiceModel:_model])
435     return NO;
436
437   e = [[self models] objectEnumerator];
438   while ((m = [e nextObject])) {
439     if (m == _model)
440       return YES;
441
442     if (![[m adaptorName] isEqualToString:[_model adaptorName]])
443       return NO;
444   }
445   
446   [self addModel:_model];
447   return YES;
448 }
449
450 - (EOEntity *)entityForObject:(id)_object {
451   return [[[self adaptor] model] entityForObject:_object];
452 }
453 - (EOEntity *)entityNamed:(NSString *)_name {
454   return [[[self adaptor] model] entityNamed:_name];
455 }
456
457 /* snapshots */
458
459 - (void)forgetSnapshotsForGlobalIDs:(NSArray *)_gids {
460   NSEnumerator *e;
461   EOGlobalID   *gid;
462
463   e = [_gids objectEnumerator];
464   while ((gid = [e nextObject]))
465     [self forgetSnapshotsForGlobalID:gid];
466 }
467 - (void)forgetSnapshotsForGlobalID:(EOGlobalID *)_gid {
468   [self notImplemented:_cmd];
469 }
470
471 - (void)recordSnapshot:(NSDictionary *)_snapshot forGlobalID:(EOGlobalID *)_gid {
472   [self notImplemented:_cmd];
473 }
474 - (void)recordSnapshots:(NSDictionary *)_snapshots {
475   NSEnumerator *gids;
476   EOGlobalID   *gid;
477
478   gids = [_snapshots keyEnumerator];
479   while ((gid = [gids nextObject]))
480     [self recordSnapshot:[_snapshots objectForKey:gid] forGlobalID:gid];
481 }
482
483 - (void)recordSnapshot:(NSArray *)_gids
484   forSourceGlobalID:(EOGlobalID *)_gid
485   relationshipName:(NSString *)_name
486 {
487   /* to-many snapshot */
488   [self notImplemented:_cmd];
489 }
490 - (void)recordToManySnapshots:(NSDictionary *)_snapshots {
491   NSEnumerator *gids;
492   EOGlobalID   *gid;
493
494   gids = [_snapshots keyEnumerator];
495   while ((gid = [gids nextObject])) {
496     NSDictionary *d;
497     NSEnumerator *relNames;
498     NSString     *relName;
499     
500     d = [_snapshots objectForKey:gid];
501     relNames = [d keyEnumerator];
502
503     while ((relName = [relNames nextObject])) {
504       [self recordSnapshot:[d objectForKey:relName]
505             forSourceGlobalID:gid
506             relationshipName:relName];
507     }
508   }
509 }
510
511 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid
512   after:(NSTimeInterval)_duration
513 {
514   NSLog(@"ERROR(%s): subclasses must override this method!",
515         __PRETTY_FUNCTION__);
516   return nil;
517 }
518
519 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid {
520   return [self snapshotForGlobalID:_gid after:NSDistantPastTimeInterval];
521 }
522
523 @end /* EODatabase(EOF2Additions) */