|
|
1.1 root 1: /* KeyGenerator.m created by cfeder on Wed 26-Oct-1994 */
2:
3: #import "KeyGenerator.h"
4: #import "ValueForKey.h"
5:
6: // Default implementation for all EOs to generate their own primary keys
7: @implementation NSObject (assignPrimaryKey)
8: - (void)assignPrimaryKeyForEntity:(EOEntity *)entity
9: {
10: NSString *primaryKeyName = [[entity primaryKeyAttributeNames] objectAtIndex:0];
11: NSNumber *value = [NSNumber numberWithUnsignedInt:[KeyGenerator nexKeyForEntity:entity]];
12: NSDictionary *pkDict = [NSDictionary dictionaryWithObjects:&value forKeys:&primaryKeyName count:1];
13: [self takeValuesFromDictionary:pkDict];
14: }
15:
16: - (void)assignPrimaryKeyIfNotAlreadyPresentForEntity:(EOEntity *)entity
17: {
18: // Figure out whether we already have a key assigned
19: BOOL hasKey = NO;
20: NSEnumerator *keys = [[entity primaryKeyAttributeNames] objectEnumerator];
21: NSString *key;
22: while (key = [keys nextObject]) {
23: id value = [self valueForKey:key];
24: if (value && (value != [EONull null]) && (![value isKindOfClass:[NSNumber class]] || ([value intValue]!=0))){
25: hasKey = YES;
26: break;
27: }
28: }
29:
30: // if we don't already have a primary key, then assign ourselves one
31: // get our current primary key values
32: if (!hasKey) {
33: [self assignPrimaryKeyForEntity:entity];
34: }
35: }
36: @end
37:
38: // Assumptions:
39: // Model contains:
40: // - login information
41: // - UniqueKey entity with attributes entity_name, and max_key
42:
43:
44: @implementation KeyGenerator
45: #define RESERVE_SET_SIZE 5
46:
47:
48: - initWithEntity:(EOEntity *)anEntity dataSource:(EODatabaseDataSource *)aDataSource
49: {
50: [super init];
51: entity = [anEntity retain];
52: dataSource = [aDataSource retain];
53: lastGranted = lastReserved = 0;
54: return self;
55: }
56:
57: - (unsigned)nextKey
58: {
59: if (lastGranted >= lastReserved) {
60: // Go out to database and reserve a block of keys
61: EOQualifier *tableNameQualifier;
62: EODatabaseChannel *channel = [dataSource databaseChannel];
63: EODatabaseContext *context = [channel databaseContext];
64: NSString *externalName = [entity externalName];
65: EOEntity *uniqueKeyEntity = [dataSource entity];
66:
67: if (!uniqueKeyEntity)
68: [NSException raise:NSInternalInconsistencyException format:@"%s: model does not contain UniqueKey entity '%@'", sel_getName(_cmd), [entity name]];
69:
70: if (![channel isOpen])
71: [channel openChannel];
72: [context beginTransaction];
73:
74: // This is the first time for this entity. We need to get the current setting.
75: if (!currentMaxRecord) {
76: // Construct the qualifier for our name
77: tableNameQualifier = [[[EOQualifier alloc] initWithEntity:uniqueKeyEntity
78: qualifierFormat:@"%A= '%@'", @"entity_name", externalName] autorelease];
79: [channel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil];
80: currentMaxRecord = [[channel fetchWithZone:[self zone]] retain];
81: [channel cancelFetch];
82: }
83:
84: if (currentMaxRecord) {
85: // attempt to update the max_key value
86: for(;;) {
87: lastGranted = [[currentMaxRecord objectForKey:@"max_key"] unsignedIntValue];
88: lastReserved = lastGranted + RESERVE_SET_SIZE;
89: [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
90:
91: if ([channel updateObject:currentMaxRecord] && [context commitTransaction])
92: break; // success!!
93:
94: // Update failed. Setup for another pass
95: [context rollbackTransaction];
96: [context beginTransaction];
97: [channel refetchObject:currentMaxRecord];
98: }
99: } else {
100: // we have to insert a record for this entity
101: currentMaxRecord = [[EOGenericRecord alloc] initWithPrimaryKey:nil entity:uniqueKeyEntity]; // retained
102: [currentMaxRecord setObject:externalName forKey:@"entity_name"];
103: lastReserved = RESERVE_SET_SIZE;
104: lastGranted = 0;
105: [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
106: NSLog(@"Entity '%@' not in UniqueKey table. Adding record: %@", [entity name], currentMaxRecord);
107: if (![channel insertObject:currentMaxRecord])
108: [NSException raise:NSInternalInconsistencyException format:@"Failed to insert UniqueKey object for entity: %@", [entity name]];
109: [context commitTransaction];
110: }
111: }
112:
113: lastGranted++;
114: return lastGranted;
115: }
116:
117: - (void)dealloc
118: {
119: [entity release];
120: [dataSource release];
121: [super dealloc];
122: }
123:
124:
125: //
126: // Class methods to automatically cache instances
127: //
128: static NSMutableDictionary *entityNameToGenerator;
129:
130: + (EODatabaseDataSource *)dataSourceForModel:(EOModel *)model
131: {
132: // use the datasource rendevous mechanism to do our caching for us
133: return [[EODatabaseDataSource alloc] initWithModelName:[model name]
134: entityName:@"unique_key"
135: databaseName:nil
136: contextName:@"_UniqueKey"
137: channelName:nil];
138: }
139:
140: + keyGeneratorForEntity:(EOEntity *)anEntity
141: {
142: KeyGenerator *kg = [entityNameToGenerator objectForKey:[anEntity name]];
143: if (!kg) {
144: EODatabaseDataSource *ds = [self dataSourceForModel:[anEntity model]];
145: if (!ds)
146: [NSException raise:NSInternalInconsistencyException format:@"%s: unable to allocation UniqueKey data source for entity '%@'", sel_getName(_cmd), [anEntity name]];
147: kg = [[KeyGenerator alloc] initWithEntity:anEntity dataSource:ds];
148:
149: if (kg) {
150: if (!entityNameToGenerator)
151: entityNameToGenerator = [NSMutableDictionary new];
152: [entityNameToGenerator setObject:kg forKey:[anEntity name]];
153: }
154: }
155: return kg;
156: }
157:
158: + (unsigned)nexKeyForEntity:(EOEntity *)anEntity
159: {
160: KeyGenerator *kg = [self keyGeneratorForEntity:anEntity];
161: if (!kg)
162: [NSException raise:NSInternalInconsistencyException format:@"%s: unable to create KeyGenerator for entity '%@'", sel_getName(_cmd), [anEntity name]];
163: return [kg nextKey];
164: }
165:
166: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.