/* 
   EOSQLExpression.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: September 1996

   This file is part of the GNUstep Database Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <Foundation/NSString.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSUtilities.h>

#include <extensions/NSException.h>
#include <extensions/DefaultScannerHandler.h>
#include <extensions/PrintfFormatScanner.h>
#include <extensions/exceptions/GeneralExceptions.h>

#include <eoaccess/common.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EORelationship.h>
#include <eoaccess/EOAdaptor.h>
#include <eoaccess/EOAdaptorContext.h>
#include <eoaccess/EOAdaptorChannel.h>
#include <eoaccess/EOQualifier.h>
#include <eoaccess/EOJoin.h>
#include <eoaccess/EOAttributeOrdering.h>
#include <eoaccess/EOSQLExpression.h>
#include <eoaccess/exceptions/EOFExceptions.h>

/*
A SQL command is generated for an entity when you fetch, insert, update or
delete an object from the database. The command includes all the attributes
from entity.

The biggest problem in generation of SQL commands is the generation of SELECT
statements, because the tables have to be properly aliased. If an entity has
only simple attributes then you have a single table in the FROM clause. If the
entity has flattened attributes, then several tables appear in the FROM list;
each one with a different alias.

The algorithm uses a dictionary that has as keys either entities or
relationships. The values in dictionary are aliases.

For a simple attribute we insert in the above dictionary the attribute's entity
as key and an alias for it.

For a flattened attribute with the following definition: `toEntity1.toEntity2.
... toEntityn.attribute', we have to assign to each toEntityi object an alias.

Let's see how each component of the SELECT command is generated. The columns
list is generated by iterating over the attributes of entity. If the attribute
is simple, its entity is looked up in the aliases dictionary. If the attribute
is flattened, the alias of attribute is the alias of toEntityn. If the
attribute is derived, each component of it is generated applying the same
rules.

The FROM list is generated like this. For each key in the aliases dictionary:
* if the key is an entity, its external name is written followed by the alias
corresponding to entity
* if the key is a relationship, the external name of its destination entity is
written followed by the alias corresponding to relationship.

A little bit complicated is the WHERE clause. For each flattened attribute we
have to generate the logic expression that selects the attribute by expressing
the joins over several entities. The algorithm starts with the first
relationship.  An additional variable, named context is used. Its initial value
is the current entity. The expression

context-alias.source-attribute join-operator
	    relationship-alias.destination-attribute

is added using the AND operator to the where clause. Then the context variable
is set to the current relationship. The algorithm continues with the next
relationship until there are no more relationships to process.

*/

@interface EOSelectScannerHandler : DefaultScannerHandler
{
    EOAttribute* attribute;
    EOAdaptor* adaptor;
    NSString* alias;
}

- (void)setAttribute:(EOAttribute*)attribute
  adaptor:(EOAdaptor*)adaptor
  alias:(NSString*)alias;

@end


@implementation EOSelectScannerHandler

- init
{
    [super init];

    specHandler['A']
	    = [self methodForSelector:@selector(convertAttribute:scanner:)];
    return self;
}

- (void)dealloc
{
    [attribute release];
    [adaptor release];
    [alias release];
    [super dealloc];
}

- (void)setAttribute:(EOAttribute*)_attribute
  adaptor:(EOAdaptor*)_adaptor
  alias:(NSString*)_alias
{
    ASSIGN(attribute, _attribute);
    ASSIGN(adaptor, _adaptor);
    ASSIGN(alias, _alias);
}

- (NSString*)convertAttribute:(va_list*)pString scanner:(FormatScanner*)scanner
{
    NSString* columnName;

    if(adaptor)
	columnName = [adaptor formatAttribute:attribute];
    else
	columnName = [attribute columnName];

    return (alias
	    ? [[[NSString alloc] initWithFormat:@"%@.%@", alias, columnName]
		    autorelease]
	    : columnName);
}

@end /* EOSelectScannerHandler */


@interface EOInsertUpdateScannerHandler : DefaultScannerHandler
{
    NSString* value;
    EOAttribute* attribute;
    EOAdaptor* adaptor;
}

- (void)setValue:(NSString*)value
  attribute:(EOAttribute*)attribute
  adaptor:(EOAdaptor*)adaptor;
@end


@implementation EOInsertUpdateScannerHandler

- init
{
    [super init];

    specHandler['V']
	    = [self methodForSelector:@selector(convertValue:scanner:)];
    return self;
}

- (void)dealloc
{
    [value release];
    [attribute release];
    [adaptor release];
    [super dealloc];
}

- (void)setValue:(NSString*)_value
  attribute:(EOAttribute*)_attribute
  adaptor:(EOAdaptor*)_adaptor
{
    ASSIGN(value, _value);
    ASSIGN(attribute, _attribute);
    ASSIGN(adaptor, _adaptor);
}

- (NSString*)convertValue:(va_list*)pString scanner:(FormatScanner*)scanner
{
    if(adaptor)
	return [adaptor formatValue:value forAttribute:attribute];
    else
	return value;
}

@end /* EOInsertUpdateScannerHandler */


@implementation EOSQLExpression

+ deleteExpressionWithQualifier:(EOQualifier*)qualifier
  channel:(EOAdaptorChannel*)channel
{
    EOSQLExpression* sqlExpression = [[[self deleteExpressionClass] new]
					autorelease];
    [sqlExpression deleteExpressionWithQualifier:qualifier channel:channel];
    return sqlExpression;
}

- deleteExpressionWithQualifier:(EOQualifier*)qualifier
  channel:(EOAdaptorChannel*)channel
{
    entity = [[qualifier entity] retain];
    adaptor = [[[channel adaptorContext] adaptor] retain];
    content = [[NSMutableString alloc] initWithFormat:@"DELETE FROM %@",
				[entity externalName]];
    [content appendString:
	    [self whereClauseForQualifier:qualifier joinExpresssion:nil]];

    [self finishBuildingExpression];

    return self;
}

+ insertExpressionForRow:(NSDictionary*)row
  entity:(EOEntity*)_entity
  channel:(EOAdaptorChannel*)channel
{
    EOSQLExpression* sqlExpression = [[[self insertExpressionClass] new]
					autorelease];
    [sqlExpression insertExpressionForRow:row entity:_entity channel:channel];
    return sqlExpression;
}

- insertExpressionForRow:(NSDictionary*)row
  entity:(EOEntity*)_entity
  channel:(EOAdaptorChannel*)channel
{
    entity = [_entity retain];
    adaptor = [[[channel adaptorContext] adaptor] retain];
    content = [NSMutableString new];

    [content appendFormat:@"INSERT INTO %@ ", [entity externalName]];
    [content appendString:[self columnListForRow:row]];
    [content appendString:[self valueListForRow:row]];

    [self finishBuildingExpression];

    return self;
}

+ selectExpressionForAttributes:(NSArray*)attributes
  lock:(BOOL)flag
  qualifier:(EOQualifier*)qualifier
  fetchOrder:(NSArray*)fetchOrder
  channel:(EOAdaptorChannel*)channel
{
    EOSQLExpression* sqlExpression = [[[self selectExpressionClass] new]
					autorelease];
    [sqlExpression selectExpressionForAttributes:attributes
		    lock:flag
		    qualifier:qualifier
		    fetchOrder:fetchOrder
		    channel:channel];
    return sqlExpression;
}

- selectExpressionForAttributes:(NSArray*)attributes
  lock:(BOOL)flag
  qualifier:(EOQualifier*)qualifier
  fetchOrder:(NSArray*)fetchOrder
  channel:(EOAdaptorChannel*)channel
{
    NSArray* relationshipPaths;
    NSString* joinExpression;

    entity = [[[attributes objectAtIndex:0] entity] retain];
    adaptor = [[[channel adaptorContext] adaptor] retain];
    content = [NSMutableString new];
    relationshipPaths = [self relationshipPathsForAttributes:attributes
			      qualifier:qualifier
			      fetchOrder:fetchOrder];
    [content appendString:[self selectListWithAttributes:attributes
								distinct:[qualifier usesDistinct]]];
    [content appendString:[self fromClause]];
    joinExpression
	    = [self joinExpressionForRelationshipPaths:relationshipPaths];
    [content appendString:
	    [self whereClauseForQualifier:qualifier
		  joinExpresssion:joinExpression]];
    [content appendString:[self orderByClauseForFetchOrder:fetchOrder]];
    if(flag)
	[content appendString:[self lockClause]];

    [self finishBuildingExpression];

    return self;
}

+ updateExpressionForRow:(NSDictionary*)row
  qualifier:(EOQualifier*)qualifier
  channel:(EOAdaptorChannel*)channel
{
    EOSQLExpression* sqlExpression = [[[self updateExpressionClass] new]
					autorelease];
    [sqlExpression updateExpressionForRow:row
		   qualifier:qualifier
		   channel:channel];
    return sqlExpression;
}

- updateExpressionForRow:(NSDictionary*)row
  qualifier:(EOQualifier*)qualifier
  channel:(EOAdaptorChannel*)channel
{
    entity = [[qualifier entity] retain];
    if (!entity)
	THROW ([[InvalidQualifierException alloc]
		    initWithFormat:@"entity for qualifier %@ is nil",
				[qualifier expressionValueForContext:nil]]);

    adaptor = [[[channel adaptorContext] adaptor] retain];
    content = [NSMutableString new];

    [content appendFormat:@"UPDATE %@ ",
				[entity externalName]];
    [content appendString:[self updateListForRow:row]];
    [content appendString:
	    [self whereClauseForQualifier:qualifier joinExpresssion:nil]];

    [self finishBuildingExpression];

    return self;
}

- (void)dealloc
{
    [entity release];
    [adaptor release];
    [entitiesAndPropertiesAliases release];
    [fromListEntities release];
    [content release];
    [super dealloc];
}

- (NSString*)selectListWithAttributes:(NSArray*)attributes
							 distinct:(BOOL)distinct
{
    NSMutableString* selectList
	    = [NSMutableString stringWithCString:"SELECT "];
    NSEnumerator* enumerator = [attributes objectEnumerator];
    BOOL first = YES;
    EOAttribute* attribute;
	if (distinct)
	  [selectList appendString:@"DISTINCT "];
    while((attribute = [enumerator nextObject])) {
	if(first)
	    first = NO;
	else
	    [selectList appendString:@", "];

	[selectList appendString:[self expressionValueForAttribute:attribute]];
    }

    return selectList;
}

- (NSString*)fromClause
{
    NSMutableString* fromClause
	    = [NSMutableString stringWithCString:"\nFROM "];
    NSEnumerator* enumerator = [fromListEntities objectEnumerator];
    BOOL first = YES;
    id key;

    /* Compute the FROM list from all the aliases found in
       entitiesAndPropertiesAliases dictionary. Note that this dictionary
       contains entities and relationships. The last ones are there for
       flattened attributes over reflexive relationships. */
    while((key = [enumerator nextObject])) {
	if(first)
	    first = NO;
	else
	    [fromClause appendString:@", "];

	if([key isKindOfClass:[EORelationship class]]) {
	    /* This key corresponds to a flattened attribute. */
	    [fromClause appendFormat:@"%@ %@",
			    [[key destinationEntity] externalName],
			    [entitiesAndPropertiesAliases objectForKey:key]];
	}
	else {
	    /* This is an EOEntity. */
	    [fromClause appendFormat:@"%@ %@",
			    [key externalName],
			    [entitiesAndPropertiesAliases objectForKey:key]];
	}
    }

    return fromClause;
}

- (NSString*)whereClauseForQualifier:(EOQualifier*)qualifier
  joinExpresssion:(NSString*)joinExpression
{
    NSMutableString* whereClause
	    = [NSMutableString stringWithCString:"\nWHERE "];
    NSString* qualifierValue = [qualifier expressionValueForContext:self];

    if((![qualifierValue length]) && ![joinExpression length])
	return @"";

    if([qualifierValue length])
	[whereClause appendFormat:@"(%@)",qualifierValue];
    if([joinExpression length]) {
	if([qualifierValue length])
	    [whereClause appendString:@" AND "];
	[whereClause appendString:joinExpression];
    }

    return whereClause;
}

- (NSString*)joinExpressionForOperator:(EOJoinOperator)operator 
  semantic:(EOJoinSemantic)semantic
{
    /* Generic SQL operators. Subclass this method if your adaptor does not
       support all of these operators or some of them use different symbols. */
    NSString* operators[EORightOuterJoin + 1][EOJoinLessThanOrEqualTo + 1] = {
	{ @"=",   @"<>",   @">",   @">=",   @"<",   @"<=" },
	{ @"*=*", @"*<>*", @"*>*", @"*>=*", @"*<*", @"*<=*" },
	{ @"*=",  @"*<>",  @"*>",  @"*>=",  @"*<",  @"*<=" },
	{ @"=*",  @"<>*",  @">*",  @">=*",  @"<*",  @"<=*" }
    };
    return operators[semantic][operator];
}

- (NSString*)joinExpressionForOperator:(EOJoinOperator)operator 
  semantic:(EOJoinSemantic)semantic
  source:source
  destination:destination
{
    return [[[NSString alloc]
		initWithFormat:@"%@ %@ %@",
		source,
		[self joinExpressionForOperator:operator semantic:semantic],
		destination]
	    autorelease];
}

- joinExpressionForRelationshipPaths:(NSArray*)relationshipPaths
{
    NSMutableString* expression = [[NSMutableString new] autorelease];
    NSEnumerator* enumerator = [relationshipPaths objectEnumerator];
    NSArray* relationshipPath;
    EORelationship* relationship;
    BOOL first = YES;

    while((relationshipPath = [enumerator nextObject])) {
	NSEnumerator* componentRelationshipsEnumerator
		= [relationshipPath objectEnumerator];
	id context = entity;

	while((relationship = [componentRelationshipsEnumerator nextObject])) {
	    NSArray* joins = [relationship joins];
	    NSEnumerator* joinsEnumerator = [joins objectEnumerator];
	    EOJoin* join;

	    /* Compute the SQL expression string corresponding to each join in
	       the relationship. */
	    while((join = [joinsEnumerator nextObject])) {
		id sourceAttribute, destinationAttribute;

		sourceAttribute = [self expressionValueForAttribute:
						    [join sourceAttribute]
					    context:context];
		destinationAttribute = [self expressionValueForAttribute:
						    [join destinationAttribute]
						context:relationship];

		if(first)
		    first = NO;
		else
		    [expression appendString:@" AND "];

		[expression appendString:
		    [self joinExpressionForOperator:[join joinOperator]
			    semantic:[join joinSemantic]
			    source:sourceAttribute
			    destination:destinationAttribute]];
	    }

	    /* Compute the next context which is the current relationship. */
	    context = relationship;
	}
    }

    return expression;
}

- (NSString*)orderByClauseForFetchOrder:(NSArray*)fetchOrder
{
    int i, count = [fetchOrder count];
    NSMutableString* orderBy;
    BOOL first = YES;

    if(!count)
	return @"";

    orderBy = (id)[NSMutableString stringWithCString:"\nORDER BY "];
    for(i = 0; i < count; i++) {
	EOAttributeOrdering* order = [fetchOrder objectAtIndex:i];
	EOOrdering ordering = [order ordering];

	if(first)
	    first = NO;
	else
	    [orderBy appendString:@", "];

	[orderBy appendFormat:@"%@",
	    [self expressionValueForAttribute:[order attribute]]];
	if(ordering != EOAnyOrder)
	    [orderBy appendString:
		([order ordering] == EOAscendingOrder ? @" ASC" : @" DESC")];
    }

    return orderBy;
}

- updateListForRow:(NSDictionary*)row 
{
    NSMutableString* expression = [NSMutableString stringWithCString:"\nSET "];
    NSEnumerator* enumerator = [row keyEnumerator];
    NSString* attributeName;
    BOOL first = YES;
    id formatScanner = [[[PrintfFormatScanner new] setAllowOnlySpecifier:YES]
			    autorelease];
    id scannerHandler = [[EOInsertUpdateScannerHandler new] autorelease];

    [formatScanner setFormatScannerHandler:scannerHandler];

    while((attributeName = [enumerator nextObject])) {
	EOAttribute* attribute = [entity attributeNamed:attributeName];
	NSString* updateFormat = [attribute updateFormat];
	NSString* columnName;
	id value;

	NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
	if(adaptor)
	    columnName = [adaptor formatAttribute:attribute];
	else columnName = [attribute columnName];

	value = [row objectForKey:attributeName];
	if(updateFormat) {
	    [scannerHandler setValue:value
			    attribute:attribute
			    adaptor:adaptor];
	    value = [formatScanner stringWithFormat:updateFormat
				   arguments:NULL];
	}
	else if(adaptor)
	    value = [adaptor formatValue:value forAttribute:attribute];

	if(first)
	    first = NO;
	else [expression appendString:@", "];

	[expression appendString:columnName];
	[expression appendString:@" = "];
	[expression appendString:value];
    }

    return expression;
}

- columnListForRow:(NSDictionary*)row
{
    NSMutableString* expression = [NSMutableString stringWithCString:"("];
    NSEnumerator* enumerator = [row keyEnumerator];
    NSString* attributeName;
    BOOL first = YES;

    while((attributeName = [enumerator nextObject])) {
	EOAttribute* attribute = [entity attributeNamed:attributeName];
	NSString* columnName;

	NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
	if(adaptor)
	    columnName = [adaptor formatAttribute:attribute];
	else
	    columnName = [attribute columnName];

	if(first)
	    first = NO;
	else [expression appendString:@", "];

	[expression appendString:columnName];
    }
    [expression appendString:@")"];

    return expression;
}

- valueListForRow:(NSDictionary*)row 
{
    NSMutableString* expression
	    = [NSMutableString stringWithCString:"\nVALUES ("];
    NSEnumerator* enumerator = [row keyEnumerator];
    NSString* attributeName;
    BOOL first = YES;
    id formatScanner = [[[PrintfFormatScanner new] setAllowOnlySpecifier:YES]
			    autorelease];
    id scannerHandler = [[EOInsertUpdateScannerHandler new] autorelease];

    [formatScanner setFormatScannerHandler:scannerHandler];

    while((attributeName = [enumerator nextObject])) {
	EOAttribute* attribute = [entity attributeNamed:attributeName];
	NSString* insertFormat = [attribute insertFormat];
	id value = [row objectForKey:attributeName];

	NSAssert1(attribute, @"attribute %@ should be non nil", attributeName);
	if(insertFormat) {
	    [scannerHandler setValue:value
			    attribute:attribute
			    adaptor:adaptor];
	    value = [formatScanner stringWithFormat:insertFormat
				   arguments:NULL];
	}
	else if(adaptor)
	    value = [adaptor formatValue:value forAttribute:attribute];

	if(first)
	    first = NO;
	else [expression appendString:@", "];

	[expression appendString:value];
    }
    [expression appendString:@")"];

    return expression;
}

- (NSArray*)relationshipPathsForAttributes:(NSArray*)attributes
  qualifier:(EOQualifier*)qualifier
  fetchOrder:(NSArray*)fetchOrder
{
    int i, count;
    NSMutableSet* entities = [[NSMutableSet new] autorelease];
    NSMutableSet* relationshipPaths = [[NSMutableSet new] autorelease];
    NSEnumerator* enumerator;
    id entityOrRelationship;

    NSAssert (entity, @"entity should be non nil");

    for(i = 0, count = [attributes count]; i < count; i++) {
	EOAttribute* attribute = [attributes objectAtIndex:i];
	if([attribute entity] != entity)
	    THROW([[InvalidAttributeException alloc]
		    initWithFormat:@"all attributes must be from the same "
			@"entity (attribute '%@' is not in '%@')",
			[attribute name],
			[entity name]]);
	if([attribute isFlattened]) {
	    id definitionArray = [attribute definitionArray];
	    NSRange range = { 0, [definitionArray count] - 1 };
	    id relationshipPath = [definitionArray subarrayWithRange:range];

	    [relationshipPaths addObject:relationshipPath];
	    [entities addObjectsFromArray:relationshipPath];
	}
	else {
	    /* attribute is normal. */
	    [entities addObject:[attribute entity]];
	}
    }

    [relationshipPaths unionSet:[qualifier relationshipPaths]];
    [entities unionSet:[qualifier additionalEntities]];

    for(i = 0, count = [fetchOrder count]; i < count; i++) {
	EOAttribute* attribute = [[fetchOrder objectAtIndex:i] attribute];
	if([attribute entity] != entity)
	    THROW([[InvalidAttributeException alloc]
		    initWithFormat:@"all attributes must be from the same "
			@"entity (attribute '%@' is not in '%@')",
			[attribute name],
			[entity name]]);
	if([attribute isFlattened]) {
	    id definitionArray = [attribute definitionArray];
	    NSRange range = { 0, [definitionArray count] - 1 };
	    id relationshipPath = [definitionArray subarrayWithRange:range];

	    [relationshipPaths addObject:relationshipPath];
	    [entities addObjectsFromArray:relationshipPath];
	}
    }

    entitiesAndPropertiesAliases = [NSMutableDictionary new];
    fromListEntities = [NSMutableArray new];
    enumerator = [entities objectEnumerator];
    i = 1;
    while((entityOrRelationship = [enumerator nextObject])) {
	NSString* alias = [NSString stringWithFormat:@"t%d", i++];
	[entitiesAndPropertiesAliases setObject:alias
				      forKey:entityOrRelationship];
	[fromListEntities addObject:entityOrRelationship];
    }

    return [relationshipPaths allObjects];
}

- (EOEntity*)entity			{ return entity; }
- finishBuildingExpression		{ return self; }
- (NSString*)lockClause			{ return @""; }

- (NSString*)expressionValueForAttribute:(EOAttribute*)attribute
  context:context
{
    NSString* alias = [entitiesAndPropertiesAliases objectForKey:context];
    NSString* columnName;

    if(adaptor)
	columnName = [adaptor formatAttribute:attribute];
    else
	columnName = [attribute columnName];

    return (alias
	    ? [[[NSString alloc] initWithFormat:@"%@.%@", alias, columnName]
		    autorelease]
	    : columnName);
}

- (NSString*)expressionValueForAttribute:(EOAttribute*)attribute
{
    if([attribute isFlattened])
	return [self expressionValueForAttributePath:
				    [attribute definitionArray]];
    else if([attribute isDerived])
	return [[attribute definitionArray] expressionValueForContext:self];

    /* attribute is a normal attribute. Its alias is the alias
       of its entity. */
    return [self expressionValueForAttribute:attribute
		 context:[attribute entity]];
}

- (NSString*)expressionValueForAttributePath:(NSArray*)definitionArray
{
    /* Take the alias of the last relationship. */
    id relationship
	= [definitionArray objectAtIndex:([definitionArray count] - 2)];
    NSString* alias = [entitiesAndPropertiesAliases objectForKey:relationship];

	if ([[definitionArray lastObject] columnName])
	  return [[[NSString alloc]
				initWithFormat:@"%@.%@", alias, [[definitionArray lastObject]
												  columnName]]
			   autorelease];
	else
	  {
		NSArray* defArray=[[definitionArray lastObject] definitionArray];
		id result = [[NSMutableString new] autorelease];
		id object=nil;
		int i=0;
		for(i=0;i<[defArray count];i++)
		  {
			object=[defArray objectAtIndex:i];
			if ([object isKindOfClass:[EOAttribute class]])
			  [result appendFormat:@"%@.%@",alias,
					  [object columnName]];
			else
			  [result appendFormat:@"%@",object];
		  };
		return result;
	  };
}

- (NSString*)expressionValueForContext:(id<EOExpressionContext>)context
{
    return content;
}

+ (Class)selectExpressionClass	{ return [EOSelectSQLExpression class]; }
+ (Class)insertExpressionClass	{ return [EOInsertSQLExpression class]; }
+ (Class)deleteExpressionClass	{ return [EODeleteSQLExpression class]; }
+ (Class)updateExpressionClass	{ return [EOUpdateSQLExpression class]; }
- (EOAdaptor*)adaptor		{ return adaptor; }

@end /* EOSQLExpression */


@implementation EOSelectSQLExpression

- (NSString*)expressionValueForAttribute:(EOAttribute*)attribute
  context:context
{
    NSString* alias = [entitiesAndPropertiesAliases objectForKey:context];
    NSString* columnName;
    NSString* selectFormat = [attribute selectFormat];

    if(selectFormat) {
	id formatScanner = [[[PrintfFormatScanner new]
				setAllowOnlySpecifier:YES]
				autorelease];
	id scannerHandler = [[EOSelectScannerHandler new] autorelease];

	[scannerHandler setAttribute:attribute adaptor:adaptor alias:alias];
	[formatScanner setFormatScannerHandler:scannerHandler];
	return [formatScanner stringWithFormat:selectFormat arguments:NULL];
    }
    else {
	if(adaptor)
	    columnName = [adaptor formatAttribute:attribute];
	else
	    columnName = [attribute columnName];

	return (alias
	    ? [[[NSString alloc] initWithFormat:@"%@.%@", alias, columnName]
		    autorelease]
	    : columnName);
    }
}

@end /* EOSelectSQLExpression */


@implementation EOInsertSQLExpression
@end /* EOInsertSQLExpression */


@implementation EOUpdateSQLExpression
@end /* EOUpdateSQLExpression */


@implementation EODeleteSQLExpression
@end /* EODeleteSQLExpression */
