/*****************************************************************************
FILE:               Oracle7Channel.m
LANGUAGE:           Objective-C
SYSTEM:             GNUstep
USER-INTERFACE:     None
DESCRIPTION

    This class is the GNUstep/db channel to the Oracle7 databases.
    Its implementation is based on the OCI v8, and should be compatible
    with, both Oracle7, Oracle8 and future Oracle9 databases.


AUTHORS
    <PJB> Pascal J. Bourguignon <pjb@imaginet.fr>
MODIFICATIONS
    1999-07-24 <PJB> Creation.
    2000-03-10 <PJB> Added setting some attributes to the model in 
                     -describeModelWithTableNames:.
BUGS
    - ...
LEGAL
    Written by:  Pascal J. Bourguignon <pjb@imaginet.fr>
    Funds from:  Orange Concept (http://www.orange-concept.com/)
    Date:        August 1999 - February 2000

    This file is part of the Oracle7 Adaptor, GNUstep Database Library.
    The Oracle7 Adaptor allows access to Oracle[678] databases from GDL.

    Copyright (C) 2000 Free Software Foundation, Inc.

    This  library is  free software;  you can  redistribute  it and/or
    modify it under the terms of the GNU Lesser 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
    Lesser General Public License for more details.

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

#include <Oracle7Channel.h>
#include <Oracle7Adaptor.h>
#include <Oracle7Context.h>
#include <Oracle7Context-ForFriends.h>
#include <Oracle7Buffer.h>
#include <Oracle7Exceptions.h>
#include <Oracle7Utilities.h>
#include <oci.h>
#include <ocidem.h>
#include <O2CI7Context.h>
#include <O2CI7Cursor.h>
#include <O2CI7Column.h>
#include <NSArray-Oracle7Adaptor_Perform.h>

//exceptions:
DECLARE_SYMBOL(Oracle7Channel_eAttributeNotInResultSet)


// -------------------------------------------------------------------------- //

@interface Oracle7Channel(Private)

    -(void)setAttribute:(EOAttribute*)attribute withDataType:(ub2)dataType;

    -(void)setExternalTypeAndClassFromRow:(NSDictionary*)row
                        withKeyDictionary:(NSDictionary*)keys
                              toAttribute:(EOAttribute*)attribute;


    -(NSMutableDictionary*)makeColumnNameToAttributeDictionaryFromAttributes:(NSArray*)attributes;

    -(NSMutableDictionary*)makeColumnNameToAttributeNameDictionaryFromAttributes:(NSArray*)attributes;



    -(void)willFetchAttributes:(NSArray*)attributes;
        /*
            POST:      fetchColumns==nil,fetchBuffers==nil,
                       attributesPassedToSelect==attributes,
                       canUseAttributesPassedToSelect
                         ==[attributes isMemberOf:[NSArray class]],
                       fetchAttributes is a non-mutable copy of 
                         attributes.
        */

    -(void)prepareFetching;
        /*
            POST:     fetchColumns!=nil,         [fetchColumns count]==c,
                      fetchAttributes!=nil,   [fetchAttributes count]==c,
                      fetchBuffers!=nil,         [fetchBuffers count]==c,
                      isFetchInProgress.
        */


    -(void)resetFetching;
        /*
            POST:      fetchAttributes==nil,
                       fetchColumns==nil,fetchBuffers==nil,
                       attributesPassedToSelect==nil,
                       canUseAttributesPassedToSelect==NO,
                       !isFetchInProgress.
        */


@end //Oracle7Channel(Private);


// -------------------------------------------------------------------------- //

@implementation Oracle7Channel(Private)

    -(void)setAttribute:(EOAttribute*)attribute withDataType:(ub2)dataType
    {
        switch(dataType){
        case SQLT_AFC:
            // char[n] 
            [attribute setExternalType:@"CHAR"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_AVC:
            // char[n+1] 
            [attribute setExternalType:@"CHARZ"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_BIN:
            // unsigned char[n] 
            [attribute setExternalType:@"RAW"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_BLOB:
            // OCILobLocator (see note 3) 
            [attribute setExternalType:@"Binary LOB"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_CHR:
            // char[n] 
            [attribute setExternalType:@"VARCHAR2"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_CLOB:
            // OCILobLocator (see note 3) 
            [attribute setExternalType:@"Character LOB"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_DAT:
            // char[7] 
            [attribute setExternalType:@"DATE"];
            [attribute setValueClassName:@"NSDate"];
            break;
        case SQLT_FILE:
            // OCILobLocator 
            [attribute setExternalType:@"FILE"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_FLT:
            // float, double 
            [attribute setExternalType:@"FLOAT"];
            [attribute setValueClassName:@"NSNumber"];
            [attribute setValueType:@"d"];
            break;
        case SQLT_INT:
            // signed short, signed int 
            // signed int, signed long 
            // signed char 
            [attribute setExternalType:@"INTEGER"];
            [attribute setValueClassName:@"NSNumber"];
            [attribute setValueType:@"i"];
            break;
        case SQLT_LAB:
            // char[n] 
            [attribute setExternalType:@"MLSLABEL"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_LBI:
            // unsigned char[n] 
            [attribute setExternalType:@"LONG RAW"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_LNG:
            // char[n] 
            [attribute setExternalType:@"LONG"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_LVB:
            // unsigned char[n+sizeof(integer)] 
            [attribute setExternalType:@"LONG VARRAW"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_LVC:
            // char[n+sizeof(integer)] 
            [attribute setExternalType:@"LONG VARCHAR"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_NTY:
            // struct 
            [attribute setExternalType:@"NAMED DATA TYPE"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_NUM:
            // unsigned char[21] 
            [attribute setExternalType:@"NUMBER"];
            [attribute setValueClassName:@"NSNumber"];
            [attribute setValueType:@"d"];
        break;
    /*
        case SQLT_ODT:
            // OCIDate (see note 2)
            [attribute setExternalType:@"OCI date type"];
            break;
    */
        case SQLT_RDD:
            // OCIRowid 
            [attribute setExternalType:@"ROWID"];
            [attribute setValueClassName:@"NSData"];
            break;
    /*
        case SQLT_REF:
            // OCIRef 
            [attribute setExternalType:@"REF"];
            break;
    */
        case SQLT_RID:
            // char[n] (see note 1)
            [attribute setExternalType:@"ROWID"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_STR:
            // char[n+1] 
            [attribute setExternalType:@"STRING"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_UIN:
            // unsigned 
            [attribute setExternalType:@"UNSIGNED INT"];
            [attribute setValueClassName:@"NSNumber"];
            [attribute setValueType:@"i"];
            break;
        case SQLT_VBI:
            // unsigned char[n+sizeof(short integer)] 
            [attribute setExternalType:@"VARRAW"];
            [attribute setValueClassName:@"NSData"];
            break;
        case SQLT_VCS:
            // char[n+sizeof(short integer)] 
            [attribute setExternalType:@"VARCHAR"];
            [attribute setValueClassName:@"NSString"];
            break;
        case SQLT_VNU:
            // char[22] 

            [attribute setExternalType:@"VARNUM"];
            [attribute setValueClassName:@"NSString"];
            break;
    /*
        case SQLT_VST:
            // OCIString (see note 2)
            [attribute setExternalType:@"OCI string type"];
            break;
    */
        default:
            [NSException raise:Oracle7Adaptor_eInternalError format:NSLocalizedStringFromTableInBundle(@"Unexpected Oracle data type %d.",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Exception raison given when an unknown data type code occurs. [1]:the unknown data type value"),dataType];
            break;
        }
    }//setAttribute:withDataType:;


    -(void)setExternalTypeAndClassFromRow:(NSDictionary*)row
                        withKeyDictionary:(NSDictionary*)keys
                              toAttribute:(EOAttribute*)attribute
    {
#define ofk(r,d,k) [r objectForKey:[d objectForKey:k]]
        NSString* dataType=         ofk(row,keys,@"DATA_TYPE");
        NSString* dataTypeMod=      ofk(row,keys,@"DATA_TYPE_MOD");
        NSNumber* dataLength=       ofk(row,keys,@"DATA_LENGTH");
        NSNumber* dataPrecision=    ofk(row,keys,@"DATA_PRECISION");
        NSNumber* dataScale=        ofk(row,keys,@"DATA_SCALE");
        NSNumber* charColDeclLength=ofk(row,keys,@"CHAR_COL_DECL_LENGTH");
        
        if([dataType compare:@"DATE"]==NSOrderedSame){
            [attribute setExternalType:dataType];
            [attribute setValueClassName:@"NSCalendarDate"];
            return;
        }

        if(([dataType compare:@"UNDEFINED"]==NSOrderedSame)
           ||([dataType compare:@"MLSLABEL"]==NSOrderedSame)){
            [attribute setExternalType:dataType];
            [attribute setValueClassName:@"NSData"];
            return;
        }

        if([dataType compare:@"NUMBER"]==NSOrderedSame){
            [attribute setValueClassName:@"NSNumber"];
            if(dataPrecision==[EONull null]){
                [attribute setExternalType:dataType];
                [attribute setValueType:@"i"];
            }else if((dataScale==[EONull null])||([dataScale longValue]==0)){
                [attribute setExternalType:
                               [NSString stringWithFormat:@"%@(%@)",
                                         dataType,dataPrecision]];
                [attribute setValueType:@"i"];
            }else{
                [attribute setExternalType:
                               [NSString stringWithFormat:@"%@(%@,%@)",
                                         dataType,dataPrecision,dataScale]];
                [attribute setValueType:@"d"];
            }
            return;
        }

        if(([dataType compare:@"BLOB"]==NSOrderedSame)
           ||([dataType compare:@"LONG RAW"]==NSOrderedSame)
           ||([dataType compare:@"RAW"]==NSOrderedSame)){
            [attribute setValueClassName:@"NSData"];
            if([dataLength longValue]==0){
                [attribute setExternalType:dataType];
            }else{
                [attribute setExternalType:[NSString stringWithFormat:@"%@(%@)",
                                                     dataType,dataLength]];
            }
            return;
        }

        if(([dataType compare:@"LONG"]==NSOrderedSame)
           ||([dataType compare:@"CLOB"]==NSOrderedSame)
           ||([dataType compare:@"NCLOB"]==NSOrderedSame)
           ||([dataType compare:@"CHAR"]==NSOrderedSame)
           ||([dataType compare:@"NCHAR"]==NSOrderedSame)
           ||([dataType compare:@"NVARCHAR2"]==NSOrderedSame)
           ||([dataType compare:@"VARCHAR2"]==NSOrderedSame)){
            [attribute setValueClassName:@"NSString"];
            if(charColDeclLength==[EONull null]){
                if([dataLength longValue]==0){
                    [attribute setExternalType:dataType];
                }else{
                    [attribute setExternalType:
                                   [NSString stringWithFormat:@"%@(%@)",
                                             dataType,dataLength]];
                }
            }else{
                [attribute setExternalType:
                               [NSString stringWithFormat:@"%@(%@)",
                                         dataType,charColDeclLength]];
            }
            return;
        }

        [NSException raise:Oracle7Adaptor_eInternalError
                     format:@"Unknown data type for the following column: %@.",
                            row];
    }//setExternalTypeAndClassFromRow:withKeyDictionary:toAttribute:;



    -(NSMutableDictionary*)makeColumnNameToAttributeDictionaryFromAttributes:(NSArray*)attributes
    {
        NSMutableDictionary* dictionary=[NSMutableDictionary 
                                  dictionaryWithCapacity:[attributes count]];
        NSEnumerator* enumeration=[attributes objectEnumerator];
        EOAttribute* attribute;
        while(nil!=(attribute=[enumeration nextObject])){
            [dictionary setObject:attribute forKey:[attribute columnName]];
        }
        return(dictionary);
    }//makeColumnNameToAttributeDictionaryFromAttributes:;


    -(NSMutableDictionary*)makeColumnNameToAttributeNameDictionaryFromAttributes:(NSArray*)attributes
    {
        NSMutableDictionary* dictionary=[NSMutableDictionary 
                                  dictionaryWithCapacity:[attributes count]];
        NSEnumerator* enumeration=[attributes objectEnumerator];
        EOAttribute* attribute;
        while(nil!=(attribute=[enumeration nextObject])){
            [dictionary setObject:[attribute name] 
                        forKey:[attribute columnName]];
        }
        return(dictionary);
    }//makeColumnNameToAttributeNameDictionaryFromAttributes:;





    -(void)willFetchAttributes:(NSArray*)attributes
    {
        [fetchColumns release];  fetchColumns=nil;
        [fetchBuffers release];  fetchBuffers=nil;
        attributesPassedToSelect=attributes;
        canUseAttributesPassedToSelect
            =[attributes isMemberOfClass:[NSArray class]];
            // We cannot trust any subclass of NSArray.
        [fetchAttributes release];
        fetchAttributes=[attributesPassedToSelect copy];
        /*
          We  make  a copy  because  attributes  could  actually be  a
          mutable  array,  and  we  don't  want  them  to  change  the
          attributes on the  fly.  Well, in that case,  we should copy
          also the  attributes, but we'll hope that  nobody would edit
          the attributes while doing a select/fetch loop.
        */
    }//willFetchAttributes:;


    -(void)prepareFetching
    {
        NSMutableArray* attributes=nil;
        EOAttribute*    attrib;
        O2CI7Column*    column;
        Oracle7Buffer*  buffer;
        NSString*       name;
        int             position;
        int             capacity=8; // default.
        int             numattrib=0;

        /*
            If we have attributes
            then preallocate the three arrays with the exact capacity,
            else preallocate them with a default capacity.

            While some column remains,

                Get a column.

                If we have an attribute, 
                then check its column name
                else make up one.

                Make a buffer.
        */

        if(fetchAttributes==nil){
            attributes=[[NSMutableArray alloc]initWithCapacity:capacity];
        }else{
            numattrib=[fetchAttributes count];
            capacity=numattrib;
        }
        [fetchColumns release];
        fetchColumns=[[NSMutableArray alloc]initWithCapacity:capacity];
        [fetchBuffers release];
        fetchBuffers=[[NSMutableArray alloc]initWithCapacity:capacity];

        
        position=1;
        column=[[[O2CI7Column alloc]
                    initWithCursor:ociCursor atPosition:position]autorelease];
        while(column!=nil){
            /*
                This loop contains a continue statement used to retry
                the body when it appears that there is more columns 
                than selected attributes.

                Therefore, in the cases where this continue may occur,
                there should be no side effect from the begining of the 
                loop until the continue statement.
            */
            if(fetchAttributes==nil){
                name=[NSString stringWithFormat:@"Attribute%d",position-1];
                attrib=[[[EOAttribute alloc]initWithName:name]autorelease];
                [attrib setColumnName:[column name]];
                //[attribute setExternalType:[column typeName]];
                // SEE: Do we use [column typeName] or [column dataType]?
                [self setAttribute:attrib withDataType:[column dataType]];
                
                [attributes addObject:attrib];
            }else{
                if(position<=numattrib){
                    attrib=[fetchAttributes objectAtIndex:position-1];
                    if([[attrib columnName]compare:[column name]
                            options:NSCaseInsensitiveSearch]!=NSOrderedSame){
                        // SEE: Localisation and formating of this message.
                        // SEE: Should we raise an exception?
                        NSLog(@"column #%d (%@) does not match "
                              @"attribute %@ (%@)",
                              position-1,[column name],
                              [attrib name],[attrib columnName]);
                    }
                }else{
                    // SEE: Localisation and formating of this message.
                    // SEE: Should we raise an exception?
                    NSLog(@"More columns than selected attributes (%d)\n",
                          numattrib);

                    // We could as well not had the selected attributes!
                    attributes=[fetchAttributes mutableCopy];
                    [fetchAttributes release]; fetchAttributes=nil;
                    canUseAttributesPassedToSelect=NO;

                    continue;
                }
            }

            /*
                The danger zone is over, we can do whatever we want from
                here down to the end of the loop.
            */
            buffer=[[[Oracle7Buffer alloc]init]autorelease];
            [buffer defineForColumn:column
                    atPosition:position
                    inCursor:ociCursor
                    asAttribute:attrib];
            /*
                In case of error in defineForColumn:..., a message would
                have been already issued, and the buffer would have been
                reset ([buffer column]==nil, [buffer attribute]==nil).
                We fill the slot the same, the buffers know how to do 
                nothing when they're in this state.
            */
            [fetchBuffers addObject:buffer];

            // We cannot do this first because of the continue above.
            [fetchColumns addObject:column];
            position++;
            column=[[[O2CI7Column alloc]
                    initWithCursor:ociCursor atPosition:position]autorelease];
        }
        if(fetchAttributes==nil){
            fetchAttributes=[attributes copy];
        }
        isFetchInProgress=YES;
        // SEE: fetchColumns is not used anywere. We should avoid creating it, the columns being retained by the buffers.
    }//prepareFetching;

 
    -(void)resetFetching
    {
        [fetchAttributes release];   fetchAttributes=nil;
        [fetchColumns release];         fetchColumns=nil;
        [fetchBuffers release];         fetchBuffers=nil;
        attributesPassedToSelect=nil;
        canUseAttributesPassedToSelect=NO;
        isFetchInProgress=NO;
    }//resetFetching;


@end //Oracle7Channel(Private);

// -------------------------------------------------------------------------- //

@implementation Oracle7Channel(ForFriends)

    -(void)willSelectAttributes:(NSArray*)attributes
    {
        [self willFetchAttributes:attributes];
    }//willSelectAttributes:;

@end // Oracle7Channel(ForFriends);

// -------------------------------------------------------------------------- //

@implementation Oracle7Channel


// NSObject methods:

    static BOOL LogExpression=NO;


    +(void)initialize
    {
        LogExpression=(getenv("ORACLE7_LOG_SQL_EXPRESSIONS")!=0);
    }//initialize;


    -(id)init
    {
        [NSException raise:Oracle7Adaptor_eBadInitializer format:@""];
        return([self initWithAdaptorContext:nil]);
    }//init;


    -(void)dealloc
    {
        if(isOpen){
            [self closeChannel];
        }
        NSAssert((ociContext==nil)
                 &&(ociCursor==nil)
                 &&(attributesPassedToSelect==nil)
                 &&(fetchAttributes==nil)
                 &&(fetchColumns==nil)
                 &&(fetchBuffers==nil),
                 @"When deallocating, once closed every "
                 @"attribute should be nil.");
        [super dealloc];
    }//dealloc;



// EOAdaptorChannel methods:

    -(id)initWithAdaptorContext:(EOAdaptorContext*)anAdaptorContext
    {
        self=[super initWithAdaptorContext:anAdaptorContext];
        if(self!=nil){
            ociContext=nil;
            ociCursor=nil;
            [self resetFetching];
        }
        return(self);
    }//initWithAdaptorContext:;



    -(BOOL)openChannel
    {
        if([super openChannel]){
            ociContext=[[(Oracle7Context*)adaptorContext createContext]retain];
            isOpen=(ociContext!=nil);
            if(isOpen){
                ociCursor=[[ociContext openCursor]retain];
                isOpen=(ociCursor!=nil);
                if(!isOpen){
                    // The error is already reported by [ociContext openCursor].
                    // [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Could not open the Oracle context (low-level) of the Oracle adaptor.\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Error message when the adaptor context cannot open the Oracle context.")]];
                    [(Oracle7Context*)adaptorContext 
                                      disposeContext:[ociContext autorelease]];
                    ociContext=nil;
                }
            }else{
                [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Could not create the Oracle context (low-level) of the Oracle adaptor.\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Error message when the adaptor context cannot create the Oracle context.")]];
            }
            return(isOpen);
        }else{
            return(NO);
        }
    }//openChannel;


    -(void)closeChannel
    {
        if(!isOpen){
            [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"The Oracle channel is already closed.\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Error message when the client tries to close a closed channel.")]];
            return;
        }
        [super closeChannel];
        if(![ociCursor close]){
            [[adaptorContext adaptor] reportError:[[ociCursor errorMessages]
                        componentsJoinedByString:@"\n"]];           
            
        }
        [ociCursor release];            ociCursor=nil;
        [(Oracle7Context*)adaptorContext 
              disposeContext:[ociContext autorelease]];
        ociContext=nil;
        [self resetFetching];
    }//closeChannel;



    -(BOOL)evaluateExpression:(NSString*)anExpression
    {
        if(LogExpression){
            NSLog(@"Oracle7Channel evaluateExpression:\"%@\"\n",anExpression);
        }
        // must not message -[super evaluateExpression:]!
        if(isFetchInProgress){
            [self resetFetching];
        }
        if([ociCursor parseSQL:anExpression]){
            if([ociCursor sqlFunctionType]==FT_SELECT){
                [self prepareFetching];
            }
            if([ociCursor execute]){
                return(YES);
            }else{
                [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"When executing this SQL statement: [%@], obtained this error: %@\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Reporting an error message when executing a SQL statement. [1]:sql statement. [2]:error message."),anExpression,[[ociCursor errorMessages] componentsJoinedByString:@"\n"]]];
                if(isFetchInProgress){
                    [self resetFetching];
                }
                return(NO);
            }
        }else{
            [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"When parsing this SQL statement: [%@], obtained this error: %@\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Reporting an error message when executing a SQL statement. [1]:sql statement. [2]:error message."),anExpression,[[ociCursor errorMessages] componentsJoinedByString:@"\n"]]];
            return(NO);
        }
    }//evaluateExpression:;



    -(NSArray*)describeResults
    {
        return(fetchAttributes);
    }//describeResults;



    -(NSMutableDictionary*)primaryFetchAttributes:(NSArray*)attributes 
            withZone:(NSZone*)zone
    {
        if(!isFetchInProgress){
            return(nil);
        }

        /*
            The  client could change  the attribute  set each  time it
            messages fetchAttributes: for a different row.  Therefore,
            we build prepare the fetch attributes, columns and buffers
            once for all  the attributes that were in  the select, and
            we get all the column  values in these buffers, but return
            an  array of  values  from here  only  for the  attributes
            specified in the attributes argument.
        */
        if([ociCursor fetch]){
            NSMutableDictionary* row;
            row=[[[NSMutableDictionary allocWithZone:zone]
                     initWithCapacity:[attributes count]]autorelease];
            if(canUseAttributesPassedToSelect
                &&(attributes==attributesPassedToSelect)){
                [fetchBuffers oracle7AdaptorMakeThemWorkOn:@selector(fillRow:) 
                              with:row];
            }else{
                /*
                    SEE: Here, we should filter out the attributes' values
                    SEE: that are not in the requested set of attributes.
                */
                [fetchBuffers oracle7AdaptorMakeThemWorkOn:@selector(fillRow:) 
                              with:row];
           }
            return(row);
        }else{
            NSMutableArray* errorMessages=[ociCursor errorMessages];
            if([errorMessages count]!=0){
                [[adaptorContext adaptor] reportError:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"When executing this SQL statement: [%@], obtained this error: %@\n",@"Oracle7Channel",Oracle7Utilities_Bundle(),@"Reporting an error message when executing a SQL statement. [1]:sql statement. [2]:error message."),@"SELECT ...",[[ociCursor errorMessages] componentsJoinedByString:@"\n"]]];
            }//else it's merely EOF.
            [self resetFetching];
            return(nil);
        }
        // don't message super!
    }//primaryFetchAttributes:withZone:;


    -(void)cancelFetch
    {
        if(![ociCursor cancel]){
            [[adaptorContext adaptor] reportError:[[ociCursor errorMessages]
                        componentsJoinedByString:@"\n"]];           
        }
        [super cancelFetch];
        [self resetFetching];
    }//canceFetch;






    -(NSArray*)describeTableNames
    {
        /*
            Exported methods should  seem "atomic".  There's no reason
            to trouble  the delegate with  will/did messages initiated
            from  internal activity.   Therefore, we  remove  it while
            doing this internal activity.
            SEE: how do adaptors developed by NeXT behave respecting this point.
        */
        NSMutableArray* result;
        id savedDelegate=[self delegate];
        [self setDelegate:nil];
        NS_DURING
            if([self evaluateExpression:
                         @"SELECT TABLE_NAME FROM USER_ALL_TABLES"]){
                result=[NSMutableArray arrayWithCapacity:8];
                if([self isFetchInProgress]){
                    NSArray* currentAttributes=[self describeResults];
                    NSString* key=[(EOAttribute*)[currentAttributes 
                                                   objectAtIndex:0] name];
                    while([self isFetchInProgress]){
                        NSDictionary* row=[self 
                                            fetchAttributes:currentAttributes 
                                            withZone:(NSZone*)0];
                        if(row!=nil){
                            [result addObject:[row objectForKey:key]];
                        }
                    }
                }
            }else{
                result=[NSMutableArray arrayWithCapacity:1];
            }
        NS_HANDLER
            [self setDelegate:savedDelegate];
            [localException raise];
        NS_ENDHANDLER
        [self setDelegate:savedDelegate];
        return(result);
    }//describeTableNames;


    -(EOEntity*)describeTableNamed:(NSString*)tableName
    {
        EOEntity* entity=nil;
        id savedDelegate=[self delegate];
        NS_DURING
            [self setDelegate:nil];
            if([self evaluateExpression:[NSString stringWithFormat:@"SELECT COLUMN_NAME,DATA_LENGTH,DATA_TYPE,DATA_TYPE_MOD,DATA_PRECISION,DATA_SCALE,CHAR_COL_DECL_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME='%@'",tableName]]){
                entity=[[[EOEntity alloc]initWithName:tableName]autorelease];
                [entity setExternalName:tableName];
                if([self isFetchInProgress]){
                    NSArray*      metaAttributes;
                    NSEnumerator* enumerator;
                    EOAttribute*  metaAttribute;
                    NSDictionary* keys;
                    NSString*     colNameAttrName=nil;

                    metaAttributes=[self describeResults];
                    keys=[self 
                        makeColumnNameToAttributeNameDictionaryFromAttributes:
                                   metaAttributes];
                    colNameAttrName=[keys objectForKey:@"COLUMN_NAME"];

                    // Let's fetch the attributes.
                    while([self isFetchInProgress]){
                        EOAttribute*    attribute;
                        NSString*       colName;
                        NSDictionary*   row;
                        row=[self fetchAttributes:metaAttributes
                                  withZone:(NSZone*)0];
                        if(row!=nil){
                            colName=[row objectForKey:colNameAttrName];
                            attribute=[[EOAttribute alloc]initWithName:colName];
                            [attribute setColumnName:colName];
                            [self setExternalTypeAndClassFromRow:row
                                  withKeyDictionary:keys
                                  toAttribute:attribute];
                            [entity addAttribute:[attribute autorelease]];
                        }else{
                            NSAssert(![self isFetchInProgress],
                               @"when fetchAttributes:withZone: returns nil, "
                               @"then isFetchInProgress should be NO.");
                        }
                    }
                }
            }
        NS_HANDLER
            [self setDelegate:savedDelegate];
            [localException raise];
        NS_ENDHANDLER
        [self setDelegate:savedDelegate];
        return(entity);
    }//describeTableNamed:;


   -(EOModel*)describeModelWithTableNames:(NSArray*)tableNames
    {
        EOModel* model=nil;
        id savedDelegate=[self delegate];
        NS_DURING
            NSString*       modelName;
            NSEnumerator*   enumerator;
            NSString*       tableName;
            [self setDelegate:nil];
            // SEE: Can we find a better name for generated model?
            modelName=[NSString stringWithFormat:@"%@-Model",
                                      [[[self adaptorContext]adaptor]name]];
            enumerator=[tableNames objectEnumerator];
            model=[[[EOModel alloc]initWithName:modelName]autorelease];
            while(nil!=(tableName=[enumerator nextObject])){
                EOEntity* entity=[self describeTableNamed:tableName];
                // SEE:
                [model addEntity:entity];
            }
            [model setAdaptorName:@"Oracle7"];
            [model setAdaptorClassName:@"Oracle7Adaptor"];
            [model setConnectionDictionary:
                       [[adaptorContext adaptor]connectionDictionary]];
        NS_HANDLER
            [self setDelegate:savedDelegate];
            [localException raise];
        NS_ENDHANDLER
        [self setDelegate:savedDelegate];
        return(model);
    }//describeModelWithTableNames:;


    -(BOOL)readTypesForEntity:(EOEntity*)anEntity
    {
        BOOL        result=NO;
        id          savedDelegate=[self delegate];
        NSString*   tableName=[anEntity externalName];
        if(tableName==nil){
            tableName=[anEntity name];
        }
        if(tableName==nil){
            return(NO);
        }
        NS_DURING
            [self setDelegate:nil];
            if([self evaluateExpression:[NSString stringWithFormat:@"SELECT COLUMN_NAME,DATA_LENGTH,DATA_TYPE,DATA_TYPE_MOD,DATA_PRECISION,DATA_SCALE,CHAR_COL_DECL_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME='%@'",tableName]]){
                if([self isFetchInProgress]){
                    NSDictionary*   attributesOfEntity;
                    NSDictionary*   keys;
                    NSArray*        metaAttributes=[self describeResults];
                    NSString*       colNameAttrName;

                    attributesOfEntity=[self 
                        makeColumnNameToAttributeDictionaryFromAttributes:
                                     [anEntity attributes]];
                    keys=[self 
                        makeColumnNameToAttributeNameDictionaryFromAttributes:
                                   metaAttributes];
                    colNameAttrName=[keys objectForKey:@"COLUMN_NAME"];

                    // Let's fetch the attributes.
                    while([self isFetchInProgress]){
                        EOAttribute*    attribute;
                        NSString*       colName;
                        NSDictionary*   row;
                        row=[self fetchAttributes:metaAttributes
                                  withZone:(NSZone*)0];
                        if(row!=nil){
                            colName=[row objectForKey:colNameAttrName];
                            attribute=[attributesOfEntity objectForKey:colName];
                            if(attribute!=nil){
                                [self setExternalTypeAndClassFromRow:row
                                      withKeyDictionary:keys
                                      toAttribute:attribute];
                            }
                        }
                    }
                }
                result=YES;
            }else{
                result=NO;
            }
        NS_HANDLER
            [self setDelegate:savedDelegate];
            [localException raise];
        NS_ENDHANDLER
        [self setDelegate:savedDelegate];
        return(result);
   }//readTypesForEntity:;


    -(BOOL)readTypeForAttribute:(EOAttribute*)anAttribute
    {
        BOOL result=NO;
        id savedDelegate=[self delegate];
        EOEntity* entity=[anAttribute entity];
        NSString* tableName;
        NSString* columnName;
        if(entity==nil){
            return(NO);
        }
        tableName=[entity externalName];
        if(tableName==nil){
            tableName=[entity name];
        }
        if(tableName==nil){
            return(NO);
        }
        columnName=[anAttribute columnName];
        if(columnName==nil){
            return(NO);
        }
        NS_DURING
            [self setDelegate:nil];
            if([self evaluateExpression:[NSString stringWithFormat:@"SELECT COLUMN_NAME,DATA_LENGTH,DATA_TYPE,DATA_TYPE_MOD,DATA_PRECISION,DATA_SCALE,CHAR_COL_DECL_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME='%@' AND COLUMN_NAME='%@'",tableName,columnName]]){
                if([self isFetchInProgress]){
                    NSArray*        metaAttributes=[self describeResults];
                    NSDictionary*   row;
                    row=[self fetchAttributes:metaAttributes 
                              withZone:(NSZone*)0];
                    if(row==nil){
                        result=NO;
                    }else{
                        NSDictionary*   keys=[self 
                          makeColumnNameToAttributeNameDictionaryFromAttributes:
                                   metaAttributes];
                        [self setExternalTypeAndClassFromRow:row
                              withKeyDictionary:keys
                              toAttribute:anAttribute];
                        result=YES;
                    }
                }else{
                    result=NO;
                }
            }else{
                result=NO;
            }
        NS_HANDLER
            [self setDelegate:savedDelegate];
            [localException raise];
        NS_ENDHANDLER
        [self setDelegate:savedDelegate];
        return(result);
    }//readTypeForAttribute:;


// Oracle7Channel methods:

    // None.


@end /* Oracle7Channel */


/*** Oracle7Channel.m                 -- 2000-02-21 07:11:22 -- PJB ***/
