
                +------------------------------------+
                |  THE GNUSTEP ORACLE 7 EOF ADAPTOR  |
                +------------------------------------+


1. Design Choices

    The GNUSTEP EOF Oracle7 Adaptor  is intended to be compatible with
    Oracle  6,  Oracle  7  and  Oracle 8  databases.   Therefore,  the
    underlying API choosen to implement the Oracle7 Adaptor is the OCI
    v.  7, which is deemed  compatible with databases from Oracle 6 to
    Oracle 8.


2. Architecture

    The Oracle7 Adaptor has two layers:

        - The     Oracle7     Adaptor    properly     (Oracle7Adaptor,
          Oracle7Context, Oracle7Channel, etc);

        - The O2CI7 (Oracle Objective C Interface) layer, which provide
          some  object abstraction  of the  OCI API,  mainly  to avoid
          coding mistakes that are inevitable with a purely procedural
          API that need many co-related parameters.

    The  Oracle7 Adaptor  layer never  accesses directly  the  OCI API
    (appart from  the C typedef  defined by oratypes.h), but  uses the
    O2CI7 objects to implement its responsibilities.


              +----------------------------------+
              | Oracl7 Adaptor                   |
              |                                  |
              |    +------------------------+    |
              |    |    Adapor  Layer       |    |
              |    +------------------------+    |
              |        :              |          |
              |        :              v          |
              |        :       +------------+    |
              |        :       |   O2CI7    |    |
              |    typedefs    +------------+    |
              |        :              |          |
              +----------------------------------+
                       :              |
                       v              v
                   +------------------------+
                   |        OCI  API        |
                   +------------------------+



2.1. The O2CI7 Layer

                                         +--------------+                     
                                         |   NSObject   |
                                         +--------------+
                                                 |                            
                                                 +                            
                                                / \               
                           +-------------------+---+-----------------+    
                           |                                         |     
  +-----------------------------------------------------+    +---------------+ 
  |                    O2CI7Object                      |    |  O2CI7Column  | 
  +-----------------------------------------------------+    +---------------+ 
  |  +  -(NSArray*)errorMessages;                       |                     
  |  +  -(void)cleanErrorMessages;                      |                     
  |  +  -(void)reportErrorMessage:(NSString*)message;   |                     
  +-----------------------------------------------------+                     
                           |                                                  
                           +                                                  
                          / \                                                 
           +-------------+---+-------------+                                  
           |                               |                                  
  +-----------------+             +----------------+                          
  |  O2CI7Context   |             |   O2CI7Cursor  |                          
  +-----------------+             +----------------+                          



    The O2CI7Object class factorize error handling for its subclasses.

            
    The O2CI7Context class encapsulates these OCI structures:

        LDA
            The LDA is 256 bytes long.
            Provides the error code.

        HDA
            The HDA must be initialized to all 0 before first call to OCI.
            The HDA is 256 bytes long on 32-bit processors and 512 bytes long
            on 64-bit processors (we can allocate it always as 512 bytes).


    The O2CI7Cursor class encapsulates this OCI structure:

        CDA
            The CDA is 64 butes long.
            - sql function code
            - row processed count
            - parse error offset
            - oci function code
            - warning flags
            - rowid

        oparse (may execute simple statements).

        obndra, obndrv, obndrn, obindps for input variables.
        odescr for queries
        odefin, odefinps for output buffers

        oexn to execute DML & transaction.

        oexfet+ofen*, oexec+ofetch*



    The O2CI7Column class is used to extract column information from a
    cursor.





2.2. The Oracle7 Adapter Layer


    [EOAdaptor]-----------connectionDictionary--[NSDictionary]
    [EOAdaptor]--0..1---------------------0..*--[EOAdaptorContext]
    [EOAdaptorContext]--0..1--------------0..*--[EOAdaptorChannel]



    The responsibilities assigned to each class are:

        - Oracle7Adapter:

            The  normal  EOAdapter  responsibilities.

            Initializes the  O2CI7 environment, but  otherwise doesn't
            directly interact with the O2CI7 layer.


        - Oracle7Context:

            The normal EOAdaptorContext responsibilities.

            Actually, delegates the hard work to an O2CI7Context
            instance. This service  context establishes the connection
            to the database.
            One O2CI7Context instance is allocated for each Oracle7Context.


        - Oracle7Channel:

            The  normal EOAdaptorChannel  responsibilities.

            Actually,  delegates  the  hard  work to  a  O2CI7Cursor
            instance, and a bunch of Oracle7Buffer instances.


        - Oracle7Buffer:

            This  class  encapsulate  a  data buffer  passed  to  this
            define.  It's  able to convert data obtained  from a fetch
            into  OpenStep instances  of  NSNumber, NSString,  NSDate,
            NSData.



4. Implementation notes

4.1. Oracle7Adapter


    The Connection Dictionary


    The  keys that  are handled  in the  connection dictionary  by the
    Oracle7 adaptor are:

          userName
          password
          hostMachine
          serverId
          NLS_LANG


    These  keys are  recognized in  a case  insensitive way,  but they
    cannot  be  duplicated.   For  example,  the presence  of  both  a
    "userName"  key   and  a  "username"  key   would  invalidate  the
    connection dictionary.


    The  connection  dictionary can  optionally  include another  key:
    NLS_LANG, which allows you to set the Oracle7 NLS_LANG environment
    variable. NLS_LANG  declares to  the Oracle7 server  the character
    set being used by the client, as well as the language in which you
    want server error messages to appear. The format is as follows:

           language_territory.characterSet

    For  example,  supplying the  value  japanese_japan.jeuc0 for  the
    NLS_LANG key tells  the server that the language  is Japanese, the
    territory  is Japan,  and  the  character set is  jeuc0. See  your
    Oracle7 documentation  for a complete list of  types available for
    this field.

    To add the NLS_LANG key and a value to your connection dictionary,
    you must manually edit your model file. For example:


        connectionDictionary = {
            hostMachine = entropy;
            password = tiger;
            serverId = sjOracle;
            userName = scott;
            NLS_LANG = american_america.us7ascii;
        };


    Using SQL*Net

           The Oracle  adaptor supports  both SQL*Net v1  and v2-style
           connection  strings.   The  version  of   SQL*Net  used  is
           determined  by the connection  string you  provide (whether
           through   the  login  panel   or  through   the  connection
           dictionary in your model file).

    To  use SQL*Net  v1, supply  a user  name, password,  host machine
    name, and server ID. This  results in a v1-style connection string
    of the form "userName/password@T:hostMachine:serverID".

    To use  SQL*Net v2, supply a  server ID, user  name, and password,
    but  omit  the host  machine  name.  This  results in  a  v2-style
    connection string that  has the form "userName/password@serverID".
    If you want to use a custom connection string, omit values for all
    of the keys except serverId in your connection dictionary. You can
    then use the  Server ID field in the Oracle  login panel to supply
    your own connection string.


    Since   this  is   such  documented   in  EOF   1.1,   the  method
    -[Oracle7Adaptor hasValidConnectionDictionary] will try to connect
    to the database, and return NO  if it cannot connect, even if this
    does  not  mean  that  the  dictionary is  mal-formed  (only,  the
    database could be unaccessible at the time this message is sent).




4.2. Oracle7Channel

If  the environment  variable ORACLE7_LOG_SQL_EXPRESSIONS  is defined,
then the SQL expressions will be logged (via NSLog).


4.2.1. selectAttributes:... / primaryFetchAttributes:...

*** SEE; THIS SECTION NEEDS TO BE UPDATED WITH CURRENT IMPLEMENTATION ***

    The superclass EOAdaptorChannel's 
    -selectAttributes:describedByQualifier:fetchOrder:lock: method does, 
    amongst other things :

        sqlexpr = [[[adaptorContext adaptor] expressionClass]
                selectExpressionForAttributes:attributes
                lock:lockFlag
                qualifier:qualifier
                fetchOrder:fetchOrder
                channel:self];
        if(![self evaluateExpression:[sqlexpr expressionValueForContext:nil]])
            return NO;

    Then, it's -fetchAttributes:withZone: will do:

        if(!row)
            row = [self primaryFetchAttributes:attributes withZone:zone];

    The attribute list passed  to -selectAttributes:... may be changed
    by the  delegate. To get the  attribute list actually  used in the
    expression,      we      could      either      override      this
    -selectAtributes:...      completely,     or      override     the
    Oracle7SQLExpression   +selectExpressionForAttributes:...,  making
    this class  message back  the channel (that  happily is  passed to
    this  +selectExpressionForAttributes:...). The  later  solution is
    choosen,    with     the    willSelectAttributes:    method     of
    Oracle7Channel(ForFriends).

    Now,  the attributes  passed  to -primaryFetchAttributes:withZone:
    will probably be the same, but not necessary. For one thing, while
    the   array   of   attributes   passed  to   the   delegate   with
    willSelectAttributes:...    is  mutable,   the  one   passed  with
    willFetchAttributes:...  is  not.  That  means that if  a delegate
    changes the  list of attributes to  select, then it  has to return
    back          a           corresponding          row          from
    adaptorChannel:willFetchAttributes:withZone:, or  else the adaptor
    won't be  able to  fill all the  requested attributes in  the row.
    But anyway,  the client could give  to fetchAttributes:withZone: a
    different set of attributes  than to selectAttributes:, and what's
    more, different at each invocation of fetchAttributes:withZone:!
    

    In primaryFetchAttributes:withZone:, we can have the following  conditions:
    
        selectedAttributes==nil
            
            when evaluateExpression:... is used outside of 
                 selectAttributes:...

            then describeResults must make up a list of attributes from
                 the list of fetched columns (with attribute name
                 ="Attribute%d" or =column name?).
    
            then the attribute list given to primaryFetchAttributes:...
                 could be the one returned by describeResults, or 
                 another one. In either cases, it could be wise to use
                 the attribute names of this list to build the row.
                 The order and number of attributes should match
                 that of the fetched columns.
    
            To ensure this, we will reset this attribute as soon as 
            the fetch loop is over.
    
    
        selectedAttributes!=nil, selectedAttributes equals  attributes
    
            when selectAttributes:... preceeds fetchAttributes:, with
                 the same attributes.
    
            then describeResults must return the selectedAttributes list.
    
            then there's no problem to correctly build the row.
    
    
        selectedAttributes!=nil, selectedAttributes differs attributes
    
            when selectAttributes:... preceeds fetchAttributes: with
                 different attributes.

            then describeResults must return the selectedAttributes list.
    
            then for each attribute in the new given list,
                     we should try to find a corresponding attribute or
                     column in the selectedAttributes or fetchedColumns.
    

    -(NSMutableDictionary*)primaryFetchAttributes:(NSArray*)attributes
            withZone:(NSZone*)zone

            Note: actually, the client could change the attributes set
                  each  time   it  messages  fetchAttributes:   for  a
                  different  row.  Therefore,  we should  rebuild  the
                  fetchBuffers each and  everytime, bare from checking
                  that the  attributes array  did not change  from one
                  invocation to the other (attributes may be a mutable
                  array).

                  The  current  implementation  will expect  the  same
                  attributes array for all the rows.


5. Naming Classes, Categories and Methods

    Contrarily  to some  other and  more modern  languages  (like Ada,
    Modula-3, or  even Java), and  in line with  rudimentary languages
    (like C), Objective-C does not manage the notion of package and of
    name space. A lot of names are in a global scope, and the only way
    to prevent  name clashes  is to prefix  these names with  a string
    that  hopefully will  be  unique.   The best  scheme  would be  to
    structure  this prefix hierarchically,  including for  example the
    Internet  domain name  of the  "owning" company,  followed  by the
    package name. Thus giving, for example:

        com_sbuilders_Oracle7Adaptor_Perform, or
        org_gnu_gnustep_Oracle7Adaptor_Perform.


    For a  library of  objects like the  Oracle7Adaptor that  could be
    linked and even dynamically linked  with a wide range of software,
    long after  both being developed and distributed,  it is important
    to  try  to  prevent  name  clashes that  would  prevent  them  to
    integrate and work together.

    That's  why naming  the adator  classes with  names that  would be
    prefixed in a scheme such as above described would be the best.

    However,  we will  consider that  'Oracle7' and  'O2CI7'  is unique
    enought  for objects  to  be dynamically  loaded  from an  Oracle7
    adaptor,  and  given  that  it's  a library  that  reproduces  the
    functionality of a widely and previously diffused one.

    On  the other  hand, when  naming categories  and methods  of base
    classes such as  NSArray or NSString, it is  much more probable to
    provoke  name clashing  if  these names  are  not unique,  because
    almost all application will  add some categories to these classes,
    and most probably for  similar purposes and reasons. Therefore the
    name of  these categories is prefixed  with 'Oracle7Adaptor_', and
    the  name  of  these  methods is  prefixed  with  'oracleAdaptor',
    giving, for example:

        in category: NSString(Oracle7Adaptor_LongLong)
            -(long long)oracle7AdaptorLongLongValue;

        and, in category: NSArray(Oracle7Adaptor_Perform)
            -(void)oracle7AdaptorMakeThemWorkOn:(SEL)selector;




9. Remaining To Do

    - Handle nested transactions.
    - Implement NLS_LANG and see brothers.
    - Implement dictionaryWithObjects:forAttributes:zone:.


9.1. Remaining Problems

    Oracle7Channel.m:584: SEE: Here, we should filter out the attributes' values
    Oracle7Channel.m:585: SEE: that are not in the requested set of attributes.

    Oracle7Adaptor.m:307: SEE: This implementation is insufficient. Let's have a look at Oracle Doc.
    Oracle7Channel.m:339: SEE: Do we use [column typeName] or [column dataType]?
    Oracle7Channel.m:348: SEE: Localisation and formating of this message.
    Oracle7Channel.m:349: SEE: Should we raise an exception?
    Oracle7Channel.m:356: SEE: Localisation and formating of this message.
    Oracle7Channel.m:357: SEE: Should we raise an exception?
    Oracle7Channel.m:765: SEE: Can we find a better name for generated model?
    Oracle7Channel.m:772: SEE:
    Oracle7Channel.m:791: SEE: how do adaptors developed by NeXT behave respecting this point.
    Oracle7Context.m:111: SEE: What can we do for nested transactions.


10. Questions

    - Is there an OpenStep way  to get and change the UNIX environment
      variables of the current process ?

    - What data formating is needed  in addition to string quoting and
      encapsulating dates in TO_DATE() functions?

    -(id)formatValue:(id)value forAttribute:(EOAttribute*)attribute
    {
        // SEE: This implementation is insufficient. 
        //      Let's have a look at Oracle Doc.
        if([self oracleTypeIsStringType:[attribute externalType]]){
            return([self quotedStringValue:value]);
        }else if([value isKindOfClass:[NSDate class]]){
            return([self dateValue:value]);
        }else{
            return([super formatValue:value forAttribute:attribute]);
        }
    }//formatValue:;







                          ---------------
                                -----
                                  .






