This is MiniSQLAdaptorChannel.m in view mode; [Download] [Up]
#import "MiniSQLAdaptorChannel.h" #import "MiniSQLAdaptorContext.h" #import "MiniSQLAdaptor.h" #import <foundation/NSArray.h> #import <foundation/NSString.h> #import <foundation/NSDictionary.h> #import <foundation/NSUtilities.h> #import <eoaccess/eoaccess.h> #import <msql.h> extern void msqlFreeResult(m_result *result); extern void msqlFieldSeek(m_result *result, int pos); #ifdef EOF10 @interface MiniSQLAdaptorChannel (EOF10Compatability) - dictionaryWithObjects:(id *)o forAttributes:(NSArray *)a zone:(NSZone *)z; @end @implementation MiniSQLAdaptorChannel (EOF10Compatability) - dictionaryWithObjects:(id *)o forAttributes:(NSArray *)a zone:(NSZone *)z { NSMutableDictionary *d; int i, count; count = [a count]; d = [[NSMutableDictionary allocWithZone:z] initWithCapacity:count]; for (i = 0; i < count; i++) { [d setObject:o[i] forKey:[[a objectAtIndex:i] name]]; } return[d autorelease]; } @end #endif @interface MiniSQLAdaptorChannel (Private) - (void) beginFetch; - (void) endFetch; @end @implementation MiniSQLAdaptorChannel (Private) - (void)beginFetch { if (!isFetchInProgress) { isFetchInProgress = YES; } } - (void)endFetch { if (isFetchInProgress) { isFetchInProgress = NO; [self setResultDescription:nil]; if (result) { msqlFreeResult(result); result = NULL; } } } @end @implementation MiniSQLAdaptorChannel - initWithAdaptor:(MiniSQLAdaptor *)anAdaptor andContext:(MiniSQLAdaptorContext *)anAdaptorContext { [super init]; sock = -1; adaptor = [anAdaptor retain]; context = [anAdaptorContext retain]; return self; } - (void)dealloc { [self closeChannel]; [adaptor release]; [context release]; [super dealloc]; } - (EOAdaptorContext *)adaptorContext // Returns the EOAdaptorContext that controls transactions for the // channel. { return context; } - (BOOL)openChannel // This method puts the channel and both its context and adaptor into a // state where they are ready to perform database operations. Returns YES // on success, NO on failure for any reason. { if ([self connectWithConnectionDictionary:[adaptor connectionDictionary]]){ [adaptor miniSQLChannelDidConnect]; return (isOpen = YES); } else { return (isOpen = NO); } } - (void)closeChannel { if (sock >= 0) { if ([self isFetchInProgress]) { [self cancelFetch]; } [adaptor miniSQLChannelDidDisconnect]; msqlClose(sock); sock = -1; isOpen = NO; } } - (BOOL)isOpen { return isOpen; } - (BOOL)connectWithConnectionDictionary:(NSDictionary *)aDictionary { NSString *hostname; NSString *database; if ((hostname = [aDictionary objectForKey:HOSTNAME]) && (database = [aDictionary objectForKey:DATABASE])) { if ([database length] == 0) { [adaptor reportError:@"Can not connect to Mini SQL without a database name."]; return NO; } if ([hostname length] == 0) { sock = msqlConnect(NULL); } else { sock = msqlConnect([hostname cString]); } if (sock < 0) { NSString *error = [NSString stringWithFormat:@"Could not connect to Mini SQL server on host %@", ([hostname length]) ? hostname : @"localhost"]; [adaptor reportError:error]; return NO; } if (msqlSelectDB(sock, ([database cString])) < 0) { NSString *error = [NSString stringWithFormat:@"Could not open Mini SQL database %@", database]; [adaptor reportError:error]; msqlClose(sock); return NO; } return YES; } else { [adaptor reportError:@"Could not decypher connection dictionary."]; return NO; } } - (NSArray *)describeEntities // Returns an array of default entities constructed from meta data // returned by the database--for example, from the catalog or system // tables. { NSMutableArray *entities = [NSMutableArray arrayWithCapacity:10]; result = msqlListTables(sock); while ((cur = msqlFetchRow(result))) { NSString *name = [NSString stringWithCString:cur[0]]; EOEntity *entity; entity = [[(EOEntity*)[EOEntity alloc] initWithName:name] autorelease]; [entity setExternalName:name]; [entity setReadOnly:NO]; [entities addObject:entity]; } msqlFreeResult(result); return entities; } - (NSArray *) describeAttributesForEntity:(EOEntity *) anEntity { NSMutableArray *attributes = [NSMutableArray arrayWithCapacity:10]; NSMutableArray *keyAttributes = [NSMutableArray arrayWithCapacity:10]; result = msqlListFields(sock,[[anEntity name] cString]); while ((curField = msqlFetchField(result))) { NSString *name = [NSString stringWithCString:curField->name]; EOAttribute *attribute; attribute = [[(EOAttribute *)[EOAttribute alloc] initWithName:name] autorelease]; [attribute setColumnName:name]; [attribute setReadOnly:NO]; switch (curField->type) { case INT_TYPE: [attribute setExternalType:@"INT"]; [attribute setValueType:[NSString stringWithCString:@encode(int)]]; [attribute setValueClassName:"NSNumber"]; break; case REAL_TYPE: [attribute setExternalType:@"REAL"]; [attribute setValueType:[NSString stringWithCString:@encode(double)]]; [attribute setValueClassName:"NSNumber"]; break; case CHAR_TYPE: [attribute setExternalType:@"CHAR"]; [attribute setValueType:[NSString stringWithCString:@encode(char*)]]; [attribute setValueClassName:"NSString"]; break; } // add the attribute to our list of primary keys, if // it is flagged as such... if (IS_PRI_KEY(curField->flags)) { [keyAttributes addObject:attribute]; } // in any case, add the attribute to our list of // attributes... [attributes addObject:attribute]; // and add it to the entity as well (this isn't // done for us)... [anEntity addAttribute:attribute]; } msqlFreeResult(result); // pass our array of primary keys ot the entity... [anEntity setPrimaryKeyAttributes:keyAttributes]; return attributes; } - (NSArray *)describeRelationshipsForEntity:(EOEntity *)entity // Returns an array of default attributes or relationships for entity, // constructed from meta-data in the database server. These methods don't // actually assign the attributes or relationships to the entity--you have // to do that yourself. See EOEntity.h for descriptions of -addAttribute: // and -addRelationship:. These methods are obsolete. { return nil; } - (NSArray *)describeTableNames // Reads and returns an array of table names from the database. This // method in conjunction with describeModelWithTableNames: is used for // building a default model in EOModeler. { NSMutableArray *names = [NSMutableArray arrayWithCapacity:10]; result = msqlListTables(sock); while ((cur = msqlFetchRow(result))) { [names addObject:[NSString stringWithCString:cur[0]]]; } msqlFreeResult(result); return names; } - (EOModel *)describeModelWithTableNames:(NSArray *)tableNames // Constructs a default model out of the database's meta data. It also // put the adaptor name and connection dictionary in the new model. This // method obsoletes describeEntities, describeAttributes..., and // describeRelationships... { EOModel *model; NSString *name; NSEnumerator *nameEnumerator = [tableNames objectEnumerator]; model = [EOModel alloc]; model = [model initWithName:[[adaptor connectionDictionary] objectForKey:DATABASE]]; [model setAdaptorName:[(NSObject *)[adaptor class] description]]; [model setConnectionDictionary:[adaptor connectionDictionary]]; while ((name = [nameEnumerator nextObject]) != nil) { EOEntity *entity; EOAttribute *attribute; NSEnumerator *attributeEnumerator; entity = [[(EOEntity *)[EOEntity alloc] initWithName:name] autorelease]; [entity setExternalName:name]; [entity setReadOnly:NO]; attributeEnumerator = [[self describeAttributesForEntity:entity] objectEnumerator]; while ((attribute = [attributeEnumerator nextObject]) != nil) { [entity addAttribute:attribute]; } [model addEntity:entity]; } return model; } - (BOOL)insertRow:(NSDictionary *)row forEntity:(EOEntity *)entity // Inserts the attributes of row into the database. row is an // NSDictionary whose keys are attribute names and whose values are the // values that will be inserted. Returns YES on success, NO on failure // for any reason. { NSString *query; BOOL didSucceed; id insertExpression; if (_delegateRespondsTo.willInsertRow) { EODelegateResponse response; row = [[[NSMutableDictionary allocWithZone:[row zone]] initWithDictionary:row] autorelease]; response = [_delegate adaptorChannel:self willInsertRow:(NSMutableDictionary *)row forEntity:entity]; if (response == EODelegateOverrides) { return YES; } if (response != EODelegateApproves) { return NO; } } if (!row || !entity) { return NO; } [self setResultDescription:nil]; insertExpression = [[adaptor expressionClass] insertExpressionForRow:row entity:entity channel:self]; query = [insertExpression expressionValueForContext:insertExpression]; didSucceed = [self evaluateExpression:query]; if (didSucceed) { if (_delegateRespondsTo.didInsertRow) { [_delegate adaptorChannel:self didInsertRow:row forEntity:entity]; } } return didSucceed; } - (BOOL)updateRow:(NSDictionary *)row describedByQualifier:(EOQualifier *)qualifier // Updates the row described by qualifier so that its values are equal to // those in row. row is an NSDictionary of attribute name/value pairs. // Returns YES on success, NO on failure for any reason. { NSString *query; BOOL didSucceed; id updateExpression; if (_delegateRespondsTo.willUpdateRow) { EODelegateResponse response; row = [[[NSMutableDictionary allocWithZone:[row zone]] initWithDictionary:row] autorelease]; response = [_delegate adaptorChannel:self willUpdateRow:(NSMutableDictionary *)row describedByQualifier:qualifier]; if (response == EODelegateOverrides) { return YES; } if (response != EODelegateApproves) { return NO; } } // FIXME: should we report an error here? if (!row || !qualifier) { return NO; } // cache a copy of the attributes we are to select... [self setResultDescription:nil]; updateExpression = [[adaptor expressionClass] updateExpressionForRow:row qualifier:qualifier channel:self]; query = [updateExpression expressionValueForContext:updateExpression]; didSucceed = [self evaluateExpression:query]; if (didSucceed && _delegateRespondsTo.didUpdateRow) { [_delegate adaptorChannel:self didUpdateRow:row describedByQualifier:qualifier]; } return YES; } - (BOOL)deleteRowsDescribedByQualifier:(EOQualifier *)qualifier // Deletes the row described by the qualifier. Returns YES on success, // NO on failure for any reason. { NSString *query; BOOL didSucceed; id deleteExpression; if (_delegateRespondsTo.willDeleteRows) { EODelegateResponse response; response = [_delegate adaptorChannel:self willDeleteRowsDescribedByQualifier:qualifier]; if (response == EODelegateOverrides) { return YES; } if (response != EODelegateApproves) { return NO; } } // FIXME: should we report an error here? if (!qualifier) { return NO; } // cache a copy of the attributes we are to select... [self setResultDescription:nil]; deleteExpression = [[adaptor expressionClass] deleteExpressionWithQualifier:qualifier channel:self]; query = [deleteExpression expressionValueForContext:deleteExpression]; didSucceed = [self evaluateExpression:query]; if (didSucceed && _delegateRespondsTo.didDeleteRows) { [_delegate adaptorChannel:self didDeleteRowsDescribedByQualifier:qualifier]; } return didSucceed; } - (BOOL) selectAttributes:(NSArray *)attributes describedByQualifier:(EOQualifier *)qualifier fetchOrder:(NSArray *)fetchOrder lock:(BOOL)flag // Selects the given attributes in rows matching the qualifier. The // selected rows compose one or more result sets, each row of which will // be returned by subsequent -fetchAttributes:withZone: messages according // to the given fetchOrder (see EOAttributeOrdering.h). If flag is YES, // the rows are locked if possible so that no other user can modify them. // Returns YES on success, NO on failure for any reason. { NSString *query; BOOL didSucceed; if (_delegateRespondsTo.willSelectAttributes) { EODelegateResponse response; NSMutableArray *a = [NSMutableArray arrayWithCapacity:[attributes count]]; [a addObjectsFromArray:attributes]; attributes = [[attributes mutableCopy] autorelease]; fetchOrder = [[fetchOrder mutableCopy] autorelease]; response = [_delegate adaptorChannel:self willSelectAttributes:(NSMutableArray *)a describedByQualifier:qualifier fetchOrder:(NSMutableArray *)fetchOrder lock:flag]; if (response == EODelegateOverrides) { return YES; } if (response != EODelegateApproves) { return NO; } } [self setResultDescription:attributes]; query = [[qualifier entity] externalQuery]; if (!query || (qualifier && ![qualifier isEmpty])) { id selectExpression = [[adaptor expressionClass] selectExpressionForAttributes:attributes lock:flag qualifier:qualifier fetchOrder:fetchOrder channel:self]; query = [selectExpression expressionValueForContext:selectExpression]; } didSucceed = [self evaluateExpression:query]; if (didSucceed) { if (_delegateRespondsTo.didSelectAttributes) { [_delegate adaptorChannel:self didSelectAttributes:attributes describedByQualifier:qualifier fetchOrder:fetchOrder lock:flag]; } } return didSucceed; } - setResultDescription:(NSArray *) attributes { [selectedAttributes autorelease]; selectedAttributes = [attributes retain]; return self; } - (NSArray *)describeResults // Returns an array of attributes describing the properties available in // the current result set, as determined by -selectAttributes:... or a // select statement evaluated by -evaluateExpression:. { return selectedAttributes; } - (BOOL)evaluateExpression:(NSString *)expression // Sends expression to the database server for evaluation. Returns YES // if no error occurs, NO if any error occurs. { EODelegateResponse response; if (_delegateRespondsTo.willEvaluateExpression) { response = [_delegate adaptorChannel:self willEvaluateExpression:(NSMutableString *) expression]; if (response == EODelegateOverrides) { return YES; } if (response != EODelegateApproves) { return NO; } } if (expression && [expression length]) { if (result) { msqlFreeResult(result); result = NULL; } if (msqlQuery(sock, [expression cString]) < 0) { NSString *error = [NSString stringWithFormat: @"Adaptor channel could not evaluate expression \"%@\".", expression]; [adaptor reportError:error]; return NO; } else { if ([self isDebugEnabled]) { NSLog(@"Adaptor channel evaluating expression \"%@\"", expression); } if (_delegateRespondsTo.didEvaluateExpression) { [_delegate adaptorChannel:self didEvaluateExpression:expression]; } result = msqlStoreResult(); if (result && result->numRows > 0) { [self beginFetch]; } return YES; } } else { return NO; } } - (NSMutableDictionary *)fetchAttributes:(NSArray *)attributes withZone:(NSZone *)zone // Fetches the next row from the result set of the last // -selectAttributes:... message and returns values for the attribute // names in attributes. When there are no more rows in the current result // set, this method returns nil, and invokes the delegate method // -adaptorChannelDidChangeResultSet: if there are more results sets. // When there are no more rows or result sets, this method returns NO, // ends the fetch, and invokes -adaptorDidFinishFetching:. // -isFetchInProgress returns YES until the fetch is cancelled or until // this method exhausts all result sets and returns nil. { NSZone *fetchZone; attributes = [[attributes mutableCopy] autorelease]; [self beginFetch]; // if the fetch succeeds, then return YES, otherwise return NO. if (_delegateRespondsTo.willFetchAttributes) { NSMutableDictionary *row; row = [_delegate adaptorChannel:self willFetchAttributes:(NSMutableArray *)attributes withZone:zone]; if (row) { return row; } } if (result && (cur = msqlFetchRow(result))) { NSMutableDictionary *row; int i, nelem = msqlNumFields(result); id *objects; // When the zone argument is NULL, use the default NSZone for the // application. This patch is to make the MSQL Adaptor behave // similarly to the Sybase, QuickBase and Oracle adaptors. if (zone != NULL) { fetchZone = zone; } else { fetchZone = NSDefaultMallocZone(); } objects = NSZoneMalloc(fetchZone, sizeof(id) * nelem); for (i = 0; i < nelem; i++) { if (cur[i] != NULL) { objects[i] = [adaptor valueForCString:cur[i] attribute:[attributes objectAtIndex:i] zone:fetchZone]; } else { objects[i] = [EONull null]; } } row = [self dictionaryWithObjects:objects forAttributes:attributes zone:zone]; if (_delegateRespondsTo.didFetchAttributes) { [_delegate adaptorChannel:self didFetchAttributes:row withZone:zone]; } NSZoneFree(fetchZone, objects); msqlFieldSeek(result, 0); return row; } else { [self endFetch]; if (_delegateRespondsTo.didFetchAttributes) { [_delegate adaptorChannelDidChangeResultSet:self]; } return (NSMutableDictionary *)nil; } } - (void)cancelFetch // Clears the result set created by the last selectAttributes:... // message, and terminates the current fetch, so that // -isFetchInProgress returns NO. { [self endFetch]; } - (BOOL)isFetchInProgress { return isFetchInProgress; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.