/* 
   CompiledAdaptorTest.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
   Date: 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/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSObjCRuntime.h>

#include <extensions/support.h>

#include <eoaccess/EOModel.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EORelationship.h>

#include <eoaccess/EOQualifier.h>
#include <eoaccess/EONull.h>

#include <eoaccess/EOAdaptor.h>
#include <eoaccess/EOAdaptorContext.h>
#include <eoaccess/EOAdaptorChannel.h>

#include "CompiledTest.h"

/*
 * Adaptor level operations
 */

@implementation CompiledTest(Adaptor)

- (void)initAdaptorObjects
{
    adaptor = [EOAdaptor adaptorWithModel:model];
    adaptorContext = [adaptor createAdaptorContext];
    adaptorChannel = [[adaptorContext createAdaptorChannel] retain];
    
    [self check:[adaptorChannel openChannel]
	format:@"could not open adaptor channel and connect to SQL server"];
}

- (void)doneAdaptorObjects
{
    [adaptorChannel closeChannel];
    [adaptorChannel release];
    adaptor = nil; adaptorContext = nil; adaptorChannel = nil;
}

- (void)enableAdaptorVerbose
{
    [adaptorChannel setDebugEnabled:YES];    
}

- (void)disableAdaptorVerbose
{
    [adaptorChannel setDebugEnabled:NO];
}

/*
 * Adaptor test helper methods
 */

- (void)beginAdaptorTransaction
{
    [self check:[adaptorContext beginTransaction]
	format:@"cannot begin transaction"];
}

- (void)commitAdaptorTransaction
{
    [self check:[adaptorContext commitTransaction]
	format:@"cannot commit transaction"];
}

/*
 *  Run adaptor test
 */

/*
	entity = entityName			- entity to modify
	keyAttributes = (attrName, ...)		- attributes for primary key
 */

- (void)adaptorModifyEntity
{
    id obj;
    EOEntity* entity;
    
    obj = [testInfo objectForKey:@"entity"];
    [self check:(obj != nil)
	format:@"adaptorModifyEntity `entity' key undefined"];
    entity = [model entityNamed:obj];
    [self check:(entity != nil)
	format:@"adaptorModifyEntity `entity' key `%@' is unknown", obj];
    
    obj = [testInfo objectForKey:@"keyAttributes"];
    if (obj) {
	[entity setPrimaryKeyAttributes:
	    [self attributesForEntity:entity names:obj]];
	[self logMessage:@"set primary key for entity"];
    }
}

/*
	clearEntities = (entityName, ...)
 */

- (void)adaptorClearEntities
{
    id obj;
    EOEntity* entity;
    NSArray* list;
    NSEnumerator* en;
    NSString* sql;
    
    list = [testInfo objectForKey:@"clearEntities"];
    [self check:(list != nil)
	format:@"adaptorClearEntities `clearEntities' key undefined"];
    
    [self beginAdaptorTransaction];
    en = [list objectEnumerator];
    while ((obj = [en nextObject])) {
	entity = [model entityNamed:obj];
	[self check:(entity != nil)
	    format:@"adaptorClearEntities: entity `%@' is unknown", obj];
    	sql = [NSString stringWithFormat:@"DELETE FROM %@", 
	    [entity externalName]];
	[self check:[adaptorChannel evaluateExpression:sql]
	    format:@"cannot execute sql `%@'", sql];
    }
    [self commitAdaptorTransaction];
    [self logMessage:@"entities cleared"];
}

/*
	entity = entityName			- entity to insert in
	insertAttributes = (attrName, ...)	- attributes to insert
	insertValues = (...)			- list of rows to insert
	insertShouldFail = YES			- if insertion should fail
 */

- (void)adaptorInsertEntity
{
    NSArray* atr_list;
    NSArray* row_list;
    EOEntity* entity;
    id obj;
    int i,n;
    BOOL shouldFail;
    
    // Get entity
    obj = [testInfo objectForKey:@"entity"];
    [self check:(obj != nil)
	format:@"adaptorInsertEntity `entity' key undefined"];
    entity = [model entityNamed:obj];
    [self check:(entity != nil)
	format:@"adaptorInsertEntity `entity' key `%@' is unknown", obj];

    // Get attribute list
    atr_list = [testInfo objectForKey:@"insertAttributes"];
    [self check:(obj != nil)
	format:@"adaptorInsertEntity `insertAttributes' key undefined"];
    atr_list = [self attributesForEntity:entity names:atr_list];
    
    // Get values list
    obj = [testInfo objectForKey:@"insertValues"];
    [self check:(obj != nil)
	format:@"adaptorInsertEntity `insertValues' key undefined"];
    
    // Make row list
    row_list = [self rowsForEntity:entity
	attributes:atr_list values:obj];
    
    // Should fail while inserting
    obj = [testInfo objectForKey:@"insertShouldFail"];
    shouldFail = (obj && [obj isEqual:@"YES"]);

    [self beginAdaptorTransaction];
    for (i = 0, n = [row_list count]; i < n; i++) {
	NSDictionary* row = [row_list objectAtIndex:i];
	if ([adaptorChannel insertRow:row forEntity:entity])
	    [self check:(!shouldFail) format:@"insert should have failed"];
	else
	    [self check:(shouldFail) format:@"insert failed"];
    }
    [self commitAdaptorTransaction];
    [self logMessage:@"rows inserted"];
}

/*
	entity = entityName			- entity to select
	selectAttributes = (attrName, ...)	- attributes to select
	selectValues = (...)			- list of rows to come
	selectQualifier = qualifier		- extra qualifier for entity
	selectOrder = ((atr, A/D), ...)		- select order
	selectLock = YES			- select with locking
 */

- (void)adaptorSelectEntity
{
    EOQualifier* qualifier;
    NSString* qualifierString;
    NSArray* atr_list;
    NSArray* row_list;
    NSMutableArray* fetched_list;
    NSArray* order_list;
    EOEntity* entity;
    id obj;
    NSDictionary* row;
    id pool;
    BOOL lock;
    int i, n;
    
    pool = [[NSAutoreleasePool alloc] init];
    
    // Get entity
    obj = [testInfo objectForKey:@"entity"];
    [self check:(obj != nil)
	format:@"adaptorSelectEntity `entity' key undefined"];
    entity = [model entityNamed:obj];
    [self check:(entity != nil)
	format:@"adaptorSelectEntity `entity' key `%@' is unknown", obj];

    // Get attribute list
    obj = [testInfo objectForKey:@"selectAttributes"];
    [self check:(obj != nil)
	format:@"adaptorSelectEntity `selectAttributes' key undefined"];
    atr_list = [self attributesForEntity:entity names:obj];
    
    // Get values list
    obj = [testInfo objectForKey:@"selectValues"];
    [self check:(obj != nil)
	format:@"adaptorSelectEntity `selectValues' key undefined"];
    row_list = [self rowsForEntity:entity
	attributes:atr_list values:obj];

    // Get locking
    obj = [testInfo objectForKey:@"selectLock"];
    lock = (obj && [obj isEqual:@"YES"]);
   
    // Get fetch order
    order_list = nil;
    obj = [testInfo objectForKey:@"selectOrder"];
    if (obj)
	order_list = [self sortOrderingForEntity:entity order:obj];

    // Get qualifier
    qualifierString = [testInfo objectForKey:@"selectQualifier"];
    qualifier = [[[EOQualifier alloc] initWithEntity:entity
	qualifierFormat:qualifierString] autorelease];
    fetched_list = [[[NSMutableArray alloc] init] autorelease];
    
    // Perform select and fetch
    [self beginAdaptorTransaction];
    [self check:[adaptorChannel selectAttributes:atr_list 
	    describedByQualifier:qualifier
	    fetchOrder:order_list
	    lock:lock]
	format:@"cannot select entity with qualifier `%@'", qualifierString];
    while ((row = [adaptorChannel fetchAttributes:atr_list withZone:NULL]))
	[fetched_list addObject:row];
    [adaptorChannel cancelFetch];
    [self commitAdaptorTransaction];
    
    // Compare rows
    n = [row_list count];
    i = [fetched_list count];
    
    [self check:(n == i)
	format:@"select got %d rows while expecting %d", i, n];
    
    for (i = 0; i < n; i++) {
	id must, got;
	
	got = [fetched_list objectAtIndex:i];
	must = [row_list objectAtIndex:i];
	
	[self check:[got isEqual:must]
	    format:@"row `%@' differs from expected `%@'", got, must];
    }
    
    [pool release];
    [self logMessage:@"rows selected"];
}

/*
	entity = entityName			- entity to update in
	updateAttributes = (attrName, ...)	- attributes to update
	updateValues = (...)			- list of rows to update
	updateKeys   = (...)			- attributes for key
	updateKeyValues = (...)			- list of key values
 */

- (void)adaptorUpdateEntity
{
    EOEntity* entity;
    NSArray* atr_list;
    NSArray* row_list;
    NSArray* key_atr;
    NSArray* key_val;
    id obj;
    id pool;
    int i, n;
    
    pool = [[NSAutoreleasePool alloc] init];
    
    // Get entity
    obj = [testInfo objectForKey:@"entity"];
    [self check:(obj != nil)
	format:@"adaptorUpdateEntity `entity' key undefined"];
    entity = [model entityNamed:obj];
    [self check:(entity != nil)
	format:@"adaptorUpdateEntity `entity' key `%@' is unknown", obj];

    // Get attribute list
    obj = [testInfo objectForKey:@"updateAttributes"];
    [self check:(obj != nil)
	format:@"adaptorUpdateEntity `updateAttributes' key undefined"];
    atr_list = [self attributesForEntity:entity names:obj];
    
    // Get values list
    obj = [testInfo objectForKey:@"updateValues"];
    [self check:(obj != nil)
	format:@"adaptorUpdateEntity `updateValues' key undefined"];
    row_list = [self rowsForEntity:entity
	attributes:atr_list values:obj];

    // Get key attributes list
    obj = [testInfo objectForKey:@"updateKeys"];
    [self check:(obj != nil)
	format:@"adaptorUpdateEntity `updateKeys' key undefined"];
    key_atr = [self attributesForEntity:entity names:obj];
    
    // Get key values list
    obj = [testInfo objectForKey:@"updateKeysValues"];
    [self check:(obj != nil)
	format:@"adaptorUpdateEntity `updateKeysValues' key undefined"];
    key_val = [self rowsForEntity:entity
	attributes:key_atr values:obj];

    [self check:[row_list count] == [key_val count]
	format:@"adaptorUpdateEntity must have matching counts in val & key"];
    
    [self beginAdaptorTransaction];
    for (i = 0, n = [row_list count]; i < n; i++) {
	NSDictionary* row = [row_list objectAtIndex:i];
	NSDictionary* key = [key_val objectAtIndex:i];
	EOQualifier* qualifier = [EOQualifier
		    qualifierForPrimaryKey:key entity:entity];
	
	[self check:(qualifier != nil)
	    format:@"adaptorUpdateEntity: nil qualifier to update row `%@'", 
		row];
	[self check:
	    [adaptorChannel updateRow:row describedByQualifier:qualifier]
	    format:@"cannot update row `%@' with qualifier `%@'",
		row, [qualifier expressionValueForContext:nil]];
    }
    [self commitAdaptorTransaction];
    
    [pool release];
    [self logMessage:@"rows updated"];
}

@end

void __compiled_adaptor_linking(void) {}
