//// NSAttributeDescription+BXPGAdditions.m// BaseTen//// Copyright (C) 2006-2008 Marko Karppinen & Co. LLC.//// Before using this software, please review the available licensing options// by visiting http://basetenframework.org/licensing/ or by contacting// us at sales@karppinen.fi. Without an additional license, this software// may be distributed only in compliance with the GNU General Public License.////// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License, version 2.0,// as published by the Free Software Foundation.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA//// $Id$//#import "NSAttributeDescription+BXPGAdditions.h"#import "NSPredicate+PGTSAdditions.h"#import "PGTSHOM.h"#import "PGTSConstantValue.h"#import "BXLogger.h"#import "PGTSFoundationObjects.h"#import "PGTSConstants.h"#import "BXEnumerate.h"#import "BXError.h"@implementationNSAttributeDescription(BXPGAdditions)+(NSString*)BXPGNameForAttributeType:(NSAttributeType)type{NSString*retval=nil;switch(type){caseNSInteger16AttributeType:retval=@"smallint";break;caseNSInteger32AttributeType:retval=@"integer";break;caseNSInteger64AttributeType:retval=@"bigint";break;caseNSDecimalAttributeType:retval=@"numeric";break;caseNSDoubleAttributeType:retval=@"double precision";break;caseNSFloatAttributeType:retval=@"real";break;caseNSStringAttributeType:retval=@"text";break;caseNSBooleanAttributeType:retval=@"boolean";break;caseNSDateAttributeType:retval=@"timestamp with time zone";break;caseNSBinaryDataAttributeType:retval=@"bytea";break;caseNSUndefinedAttributeType:caseNSTransformableAttributeType:default:break;}returnretval;}-(NSMutableSet*)BXPGParentPredicates{NSString*name=[selfname];NSEntityDescription*parent=[selfentity];NSMutableSet*parentPredicates=[NSMutableSetset];while(nil!=(parent=[parentsuperentity])){NSAttributeDescription*parentAttribute=[[parentattributesByName]objectForKey:name];if(!parentAttribute)break;[parentPredicatesaddObjectsFromArray:[parentAttributevalidationPredicates]];}if(![parentPredicatescount])parentPredicates=nil;returnparentPredicates;}-(void)BXPGPredicate:(NSPredicate*)givenPredicatelengthExp:(NSExpression*)lengthExpmaxLength:(NSInteger*)maxLength{if([givenPredicateisKindOfClass:[NSComparisonPredicateclass]]){NSComparisonPredicate*predicate=(NSComparisonPredicate*)givenPredicate;NSExpression*lhs=[predicateleftExpression];NSExpression*rhs=[predicaterightExpression];NSPredicateOperatorTypeoperator=[predicatepredicateOperatorType];BOOLdoTest=NO;NSIntegervalue=0;if([lhsisEqual:lengthExp]&&NSConstantValueExpressionType==[rhsexpressionType]){value=[[rhsconstantValue]integerValue];switch(operator){caseNSLessThanPredicateOperatorType:value--;caseNSLessThanOrEqualToPredicateOperatorType:doTest=YES;break;}}elseif([rhsisEqual:lengthExp]&&NSConstantValueExpressionType==[lhsexpressionType]){value=[[lhsconstantValue]integerValue];switch(operator){caseNSGreaterThanPredicateOperatorType:value--;caseNSGreaterThanOrEqualToPredicateOperatorType:doTest=YES;break;}}if(doTest&&value<*maxLength)*maxLength=value;}}-(NSInteger)BXPGMaxLength{NSIntegerretval=NSIntegerMax;NSMutableSet*predicates=[selfBXPGParentPredicates];[predicatesaddObjectsFromArray:[selfvalidationPredicates]];NSExpression*lengthExp=[NSExpressionexpressionForKeyPath:@"length"];[[predicatesPGTSVisit:self]BXPGPredicate:nillengthExp:lengthExpmaxLength:&retval];if(retval<=0)retval=NSIntegerMax;returnretval;}-(NSString*)BXPGValueType{NSString*retval=nil;NSAttributeTypeattrType=[selfattributeType];NSIntegermaxLength=NSIntegerMax;if(NSStringAttributeType==attrType&&NSIntegerMax!=(maxLength=[selfBXPGMaxLength]))retval=[NSStringstringWithFormat:@"varchar (%d)",maxLength];elseretval=[[selfclass]BXPGNameForAttributeType:attrType];returnretval;}staticNSExpression*CharLengthExpression(NSString*name){NSString*fcall=[NSStringstringWithFormat:@"char_length (\"%@\")",name];PGTSConstantValue*value=[PGTSConstantValuevalueWithString:fcall];NSExpression*retval=[NSExpressionexpressionForConstantValue:value];returnretval;}//FIXME: this could be moved to NSKeyPathExpression handling in NSExpression+PGTSAdditions.-(NSPredicate*)BXPGTransformPredicate:(NSPredicate*)givenPredicate{NSPredicate*retval=givenPredicate;NSAttributeTypeattrType=[selfattributeType];//FIXME: handle more special cases? Are there any?switch(attrType){caseNSStringAttributeType:{//FIXME: this could be generalized. We don't iterate subpredicates because Xcode data modeler doesn't create compound predicates.if([givenPredicateisKindOfClass:[NSComparisonPredicateclass]]){NSComparisonPredicate*predicate=(NSComparisonPredicate*)givenPredicate;NSExpression*lhs=[predicateleftExpression];NSExpression*rhs=[predicaterightExpression];NSExpression*lenghtExp=[NSExpressionexpressionForKeyPath:@"length"];if([lhsisEqual:lenghtExp]){NSExpression*lhs=CharLengthExpression([selfname]);retval=[NSComparisonPredicatepredicateWithLeftExpression:lhsrightExpression:rhsmodifier:[predicatecomparisonPredicateModifier]type:[predicatepredicateOperatorType]options:[predicateoptions]];}elseif([rhsisEqual:lenghtExp]){NSExpression*rhs=CharLengthExpression([selfname]);retval=[NSComparisonPredicatepredicateWithLeftExpression:lhsrightExpression:rhsmodifier:[predicatecomparisonPredicateModifier]type:[predicatepredicateOperatorType]options:[predicateoptions]];}else{//FIXME: report the error in some other way. We don't understand other key paths than length.BXLogError(@"Predicate %@ wasn't understood.",[predicatepredicateFormat]);retval=nil;}}break;}default:break;}returnretval;}-(NSArray*)BXPGAttributeConstraintsInSchema:(NSString*)schemaName{NSString*name=[selfname];NSString*entityName=[[selfentity]name];NSMutableArray*retval=[NSMutableArrayarrayWithCapacity:2];if(![selfisOptional]){NSString*format=@"ALTER TABLE \"%@\".\"%@\" ALTER COLUMN \"%@\" SET NOT NULL;";[retvaladdObject:[NSStringstringWithFormat:format,schemaName,entityName,name]];}returnretval;}-(NSArray*)BXPGConstraintsForValidationPredicatesInSchema:(NSString*)schemaNameconnection:(PGTSConnection*)connection{NSString*name=[selfname];NSString*entityName=[[selfentity]name];NSArray*givenValidationPredicates=[selfvalidationPredicates];NSMutableArray*retval=[NSMutableArrayarrayWithCapacity:[givenValidationPredicatescount]];//Check parent's validation predicates so that we don't create the same predicates two times.NSSet*parentPredicates=[selfBXPGParentPredicates];NSString*format=@"ALTER TABLE \"%@\".\"%@\" ADD CHECK (%@);";//Patch by Tim Bedford 2008-08-06.BXEnumerate(currentPredicate,e,[givenValidationPredicatesobjectEnumerator]){//Skip if parent has this one.if([parentPredicatescontainsObject:currentPredicate])continue;//Check that the predicate may be resolved in the database.currentPredicate=[selfBXPGTransformPredicate:currentPredicate];if(!currentPredicate)continue;NSMutableDictionary*ctx=[NSMutableDictionarydictionaryWithObjectsAndKeys:connection,kPGTSConnectionKey,[NSNumbernumberWithBool:YES],kPGTSExpressionParametersVerbatimKey,nil];NSString*SQLExpression=[currentPredicatePGTSExpressionWithObject:namecontext:ctx];NSMutableString*constraint=[NSMutableStringstringWithFormat:format,schemaName,entityName,SQLExpression];[retvaladdObject:constraint];}returnretval;}-(NSString*)BXPGAttributeDefinition:(PGTSConnection*)connection{NSString*typeDefinition=[selfBXPGValueType];NSString*addition=@"";iddefaultValue=[selfdefaultValue];if(defaultValue){NSString*defaultExp=[defaultValuePGTSExpressionOfType:[selfattributeType]connection:connection];if(defaultExp)addition=[NSStringstringWithFormat:@"DEFAULT %@",defaultExp];}return[NSStringstringWithFormat:@"\"%@\" %@ %@",[selfname],typeDefinition,addition];}staticNSError*ImportError(NSString*message,NSString*reason){Expect(message);Expect(reason);//FIXME: set the domain and the code.NSDictionary*userInfo=[NSDictionarydictionaryWithObjectsAndKeys:message,NSLocalizedFailureReasonErrorKey,reason,NSLocalizedRecoverySuggestionErrorKey,nil];NSError*retval=[BXErrorerrorWithDomain:@""code:0userInfo:userInfo];returnretval;}-(BOOL)BXCanAddAttribute:(NSError**)outError{BOOLretval=NO;NSString*errorFormat=@"Skipped attribute %@ in %@.";NSError*localError=nil;if(![selfisTransient]){switch([selfattributeType]){caseNSUndefinedAttributeType:{NSString*errorString=[NSStringstringWithFormat:errorFormat,[selfname],[[selfentity]name]];localError=ImportError(errorString,@"Attributes with undefined type are not supported.");break;}caseNSTransformableAttributeType:{NSString*errorString=[NSStringstringWithFormat:errorFormat,[selfname],[[selfentity]name]];localError=ImportError(errorString,@"Attributes with transformable type are not supported.");break;}default:retval=YES;break;}}if(outError)*outError=localError;returnretval;}@end