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.