Search code examples
ioscore-datansfetchrequestnsexpression

NSExpression using sqrt: function results in NSInvalidArgumentException


I'm getting an NSInvalidArgumentException:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported function type passed to SQL store'

When trying to use an NSExpression to take the square root of a nested NSExpression which is using the "multiply:by:" function.

My code looks like this:

NSManagedObjectContext *context=[[BDCoreDataController sharedController] mainManagedObjectContext];
NSFetchRequest *theRequest=[[NSFetchRequest alloc] init];

[theRequest setEntity:[NSEntityDescription entityForName:@"BDCompanyEntity" inManagedObjectContext:context]];
theRequest.resultType=NSDictionaryResultType;

NSExpression *constantExpression=[NSExpression expressionForConstantValue:[NSNumber numberWithDouble:22.5]];
NSExpression *firstKeyPath=[NSExpression expressionForKeyPath:@"epsTTM"];
NSExpression *secondKeyPath=[NSExpression expressionForKeyPath:@"bookValuePerShare"];

NSExpression *firstMultiplyExpression=[NSExpression expressionForFunction:@"multiply:by:" arguments:@[firstKeyPath,secondKeyPath]];

NSExpression *secondMultiplyExpression=[NSExpression expressionForFunction:@"multiply:by:" arguments:@[firstMultiplyExpression,constantExpression]];

NSExpression *sqrtExpression=[NSExpression expressionForFunction:@"sqrt:" arguments:@[secondMultiplyExpression]];

NSExpressionDescription *expressionDescription=[[NSExpressionDescription alloc] init];
[expressionDescription setName:@"grahamNumber"];
[expressionDescription setExpression:sqrtExpression];
[expressionDescription setExpressionResultType:NSDoubleAttributeType];

[theRequest setPropertiesToFetch:@[expressionDescription,@"name"]];

NSError *fetchError;
NSArray *grahams=[context executeFetchRequest:theRequest error:&fetchError];

Executing the fetch with just trying to evaluate the nested "multiply:by:" works fine with this code:

NSManagedObjectContext *context=[[BDCoreDataController sharedController] mainManagedObjectContext];
NSFetchRequest *theRequest=[[NSFetchRequest alloc] init];

[theRequest setEntity:[NSEntityDescription entityForName:@"BDCompanyEntity" inManagedObjectContext:context]];
theRequest.resultType=NSDictionaryResultType;

NSExpression *constantExpression=[NSExpression expressionForConstantValue:[NSNumber numberWithDouble:22.5]];
NSExpression *firstKeyPath=[NSExpression expressionForKeyPath:@"epsTTM"];
NSExpression *secondKeyPath=[NSExpression expressionForKeyPath:@"bookValuePerShare"];

NSExpression *firstMultiplyExpression=[NSExpression expressionForFunction:@"multiply:by:" arguments:@[firstKeyPath,secondKeyPath]];

NSExpression *secondMultiplyExpression=[NSExpression expressionForFunction:@"multiply:by:" arguments:@[firstMultiplyExpression,constantExpression]];

NSExpressionDescription *expressionDescription=[[NSExpressionDescription alloc] init];
[expressionDescription setName:@"grahamNumber"];
[expressionDescription setExpression: secondMultiplyExpression];
[expressionDescription setExpressionResultType:NSDoubleAttributeType];

[theRequest setPropertiesToFetch:@[expressionDescription,@"name"]];

NSError *fetchError;
NSArray *grahams=[context executeFetchRequest:theRequest error:&fetchError];

At first I thought that perhaps the "sqrt" NSExpression was having trouble with the nested NSExpression. So I tried just a constant NSExpression. The following code tries to just take evaluate a constant in the sqrt NSExpression. It causes the same NSInvalidArguementException

NSManagedObjectContext *context=[[BDCoreDataController sharedController] mainManagedObjectContext];
NSFetchRequest *theRequest=[[NSFetchRequest alloc] init];

[theRequest setEntity:[NSEntityDescription entityForName:@"BDCompanyEntity" inManagedObjectContext:context]];
theRequest.resultType=NSDictionaryResultType;

NSExpression *constantExpression=[NSExpression expressionForConstantValue:[NSNumber numberWithDouble:22.5]];

NSExpression *sqrtExpression=[NSExpression expressionForFunction:@"sqrt:" arguments:@[constantExpression]];

NSExpressionDescription *expressionDescription=[[NSExpressionDescription alloc] init];
[expressionDescription setName:@"grahamNumber"];
[expressionDescription setExpression:sqrtExpression];
[expressionDescription setExpressionResultType:NSDoubleAttributeType];

[theRequest setPropertiesToFetch:@[expressionDescription,@"name"]];

NSError *fetchError;
NSArray *grahams=[context executeFetchRequest:theRequest error:&fetchError];

any ideas what could be causing the problem?

Thanks

toolman


Solution

  • Not every expression that's valid for NSExpression is valid for use when fetching from Core Data when using a SQLite store. Unfortunately sqrt: is not supported for this kind of use. It would probably work with a binary store, but that loses many of the advantages of Core Data (it would mean loading all data into memory at once, for example).

    What's especially annoying is that there's no documented list that indicates which functions are supported for use with Core Data. But the error message is exactly correct: Although this function is valid for use with NSExpression, it's not valid for use with a SQLite persistent store.