home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / EODEV.Z / ODBCAdaptor.m < prev    next >
Encoding:
Text File  |  1996-09-10  |  18.8 KB  |  587 lines

  1. /*
  2.    ODBCAdaptor.m
  3.  
  4.    Copyright (c) 1996 NeXT Software, Inc.  All rights reserved.
  5.  
  6.    IMPORTANT:  This NeXT software is supplied to you in consideration of your agreement
  7.    to the terms of the NeXT Software License Agreement stated in the file ODBC_LICENSE.rtf
  8.    provided with the software.  Your use of the software is governed by such terms.
  9.    Do not use, install, or make copies of the software if you do not agree to such terms.
  10.  
  11.    THIS SOFTWARE IS FURNISHED ON AN "AS-IS" BASIS. NeXT MAKES NO WARRANTIES OF ANY KIND,
  12.    EITHER EXPRESS OR IMPLIED, AS TO ANY MATTER WHATSOEVER, INCLUDING WITHOUT LIMITATION
  13.    THE CONDITION, MERCHANTABILITY, OR FITNESS FOR ANY PARTICULAR PURPOSE OF THIS SOFTWARE.
  14.    NeXT DOES NOT ASSUME ANY LIABILITY REGARDING USE OF, OR ANY DEFECT IN, THIS SOFTWARE.
  15.    IN NO EVENT SHALL NeXT BE LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
  16.    DAMAGES, EVEN IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  17. */
  18.  
  19. #import "ODBCPrivate.h"
  20.  
  21. //
  22. // The unique ODBCEnvironment variable. Shared by
  23. // all the instances of ODBCAdaptor
  24. //
  25. static HENV odbcEnvironment = NULL;
  26.  
  27. @implementation ODBCAdaptor
  28.  
  29. + (void)initialize
  30. {
  31.     //
  32.     // Initialize once for all the odbcEnvironment variable
  33.     //
  34.     if (!odbcEnvironment && SQLAllocEnv(&odbcEnvironment) != SQL_SUCCESS)
  35.         [NSException raise:EOGeneralAdaptorException format:@"Unable to allocate ODBC environment"];
  36. }
  37.  
  38. - initWithName:(NSString *)name
  39. {
  40.     //
  41.     // This method does nothing. I kept it here
  42.     // only as a reminder: this is the designated 
  43.     // initializer
  44.     //
  45.     [super initWithName:name];
  46.     return self;
  47. }
  48.  
  49. - (EOAdaptorContext *)createAdaptorContext
  50. {
  51.     return [[[ODBCContext allocWithZone:[self zone]] initWithAdaptor:self] autorelease];
  52. }
  53.  
  54. - (Class)defaultExpressionClass
  55. {
  56.     return [ODBCSQLExpression class];
  57. }
  58.  
  59. - (BOOL)isValidQualifierType:(NSString *)typeName model:(EOModel *)model
  60. {
  61.     NSDictionary *dict = [ODBCAdaptor typeInfoForModel:model];
  62.     NSDictionary *typeInfo;
  63.  
  64.     typeInfo = [dict objectForKey:typeName];
  65.  
  66.     if(!typeInfo) {
  67.         return NO;
  68.     }
  69.  
  70.     return [[typeInfo objectForKey:@"isSearchable"] isEqualToString:@"YES"];
  71. }
  72.  
  73. - (void)assertConnectionDictionaryIsValid
  74. {
  75.     // If there is already an open channel, there is no need to
  76.     // retest the connection
  77.     //
  78.     if(![self hasOpenChannels]) {
  79.         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  80.         ODBCContext *context = [self createAdaptorContext];
  81.         ODBCChannel *channel = [context createAdaptorChannel];
  82.         
  83.         [channel openChannel];
  84.         [channel closeChannel]; // Just try to connect to the database
  85.  
  86.         [pool release];
  87.         // The channel is autoreleased, so everything should go away
  88.         // with the pool
  89.     }
  90. }
  91.  
  92. - (NSString *)odbcConnectionString
  93. {
  94.     NSString *userName, *password, *dataSource, *connectionString;
  95.  
  96.     //
  97.     // If there is a connection string in the dictionary, just use it.
  98.     //
  99.     if ((connectionString = [_connectionDictionary objectForKey:connectionStringKey]) != nil) {
  100.         return connectionString;
  101.     }
  102.  
  103.     //
  104.     // Get connection information     
  105.     //
  106.     dataSource = [_connectionDictionary objectForKey:dataSourceKey];
  107.     userName = [_connectionDictionary objectForKey:userNameKey];
  108.     password = [_connectionDictionary objectForKey:passwordKey];
  109.  
  110.     // construct connection string
  111.     return [NSString stringWithFormat:@"DSN=%@;UID=%@;PWD=%@", dataSource ? dataSource: @"", userName ? userName : @"", password ? password : @""];
  112. }
  113.  
  114. - (void *)odbcEnvironment { return odbcEnvironment; }
  115.  
  116. @end
  117.  
  118. @implementation ODBCAdaptor (ODBCTypeMapping)
  119.  
  120. + (NSArray *)externalTypesWithModel:(EOModel *)model
  121. {
  122.     //
  123.     // Just extract the information from the model file
  124.     //
  125.     return [[ODBCAdaptor typeInfoForModel:model] allKeys];
  126. }
  127.  
  128.  
  129. //
  130. // The following table describe, for every ODBC type, the preferred
  131. // corresponding Foundation class.
  132. //
  133. typedef struct {
  134.     int odbcType;
  135.     NSString *odbcTypeString;
  136.     NSString *internalType;
  137.     NSString *valueType; // For NSNumber, mostly
  138. } typeStruct;
  139.  
  140. static typeStruct defaultCorrespondingTypes[] = {
  141.     { SQL_VARCHAR,       @"VARCHAR",        @"NSString", nil },
  142.     { SQL_CHAR,          @"CHAR",           @"NSString", nil },
  143.     { SQL_LONGVARCHAR,   @"LONG VARCHAR",   @"NSString", nil },
  144.     { SQL_DECIMAL,       @"DECIMAL",        @"NSDecimalNumber", nil },
  145.     { SQL_NUMERIC,       @"NUMERIC",        @"NSDecimalNumber", nil },
  146.     { SQL_BIGINT,        @"BIGINT",         @"NSNumber", @"l" }, // long L
  147.     { SQL_SMALLINT,      @"SMALLINT",       @"NSNumber", @"s" }, // short S
  148.     { SQL_INTEGER,       @"INTEGER",        @"NSNumber", @"i" }, // integer I
  149.     { SQL_REAL,          @"REAL",           @"NSNumber", @"f" }, // float
  150.     { SQL_FLOAT,         @"FLOAT",          @"NSNumber", @"d" }, // double
  151.     { SQL_DOUBLE,        @"DOUBLE",         @"NSNumber", @"d" }, // double
  152.     { SQL_BIT,           @"BIT",            @"NSNumber", @"c" }, // char
  153.     { SQL_TINYINT,       @"TINYINT",        @"NSNumber", @"c" }, // char C
  154.     { SQL_VARBINARY,     @"VARBINARY",      @"NSData", nil },
  155.     { SQL_BINARY,        @"BINARY",         @"NSData", nil },
  156.     { SQL_LONGVARBINARY, @"LONG VARBINARY", @"NSData", nil },
  157.     { SQL_TIMESTAMP,     @"TIMESTAMP",      @"NSCalendarDate", nil },
  158.     { SQL_DATE,          @"DATE",           @"NSCalendarDate", nil },
  159.     { SQL_TIME,          @"TIME",           @"NSCalendarDate", nil },
  160.     { SQL_TYPE_NULL, nil, nil}
  161. };
  162.  
  163. //
  164. // Possible optimisation here ! Use two map tables to hash the name to SQL
  165. // and vice versa.
  166. //
  167. + (NSString *)stringRepresentationForOdbcType:(int)type
  168. {
  169.     unsigned i = 0;
  170.     while(defaultCorrespondingTypes[i].odbcType != SQL_TYPE_NULL) {
  171.         if(defaultCorrespondingTypes[i].odbcType == type) {
  172.            return defaultCorrespondingTypes[i].odbcTypeString;
  173.         }
  174.         i += 1;
  175.     }
  176.  
  177.     return nil;
  178. }
  179.  
  180. + (int)odbcTypeForStringRepresentation:(NSString *)type
  181. {
  182.     unsigned i = 0;
  183.  
  184.     while(defaultCorrespondingTypes[i].odbcType != SQL_TYPE_NULL) {
  185.         if([defaultCorrespondingTypes[i].odbcTypeString isEqualToString:type])
  186.             return defaultCorrespondingTypes[i].odbcType;
  187.         i += 1;
  188.     }
  189.  
  190.     return SQL_TYPE_NULL;
  191. }
  192.  
  193. + (NSString *)odbcTypeForExternalType:(NSString *)extType model:(EOModel *)model
  194.  
  195. {
  196.     NSDictionary *dict = [ODBCAdaptor typeInfoForModel:model];
  197.     NSDictionary *typeInfo;
  198.  
  199.     //
  200.     // Extract the relevant typeInfo from the model
  201.     //
  202.     typeInfo = [dict objectForKey:extType];
  203.     if(!typeInfo) {
  204.         return nil;
  205.     }
  206.  
  207.     //
  208.     // The first item of the list is the prefered one
  209.     //
  210.      return [[typeInfo objectForKey:@"defaultODBCType"] objectAtIndex:0];
  211. }
  212.  
  213. + (NSString *)internalTypeForExternalType:(NSString *)extType model:(EOModel *)model
  214. {
  215.     NSString *odbcType;
  216.     unsigned int i = 0;
  217.  
  218.     odbcType = [self odbcTypeForExternalType:extType model:model];
  219.     //
  220.     // Search for it in my table.
  221.     //
  222.     while(defaultCorrespondingTypes[i].internalType) {
  223.         if([defaultCorrespondingTypes[i].odbcTypeString isEqualToString:odbcType]) {
  224.             return defaultCorrespondingTypes[i].internalType;
  225.         }
  226.     }
  227.  
  228.     return nil;
  229. }
  230.  
  231. + (NSString *)externalTypeForOdbcType:(int)type model:(EOModel *)model;
  232. {
  233.     NSString *odbcType = nil;
  234.     unsigned i = 0, c;
  235.     NSDictionary *typeInfo;
  236.     NSArray *allKeys;
  237.     NSString *best = nil;
  238.     unsigned position = UINT_MAX;
  239.     
  240.     //
  241.     // First, get the string representation for the odbc type
  242.     //
  243.     odbcType = [self stringRepresentationForOdbcType:type];
  244.     if(!odbcType) return nil;
  245.  
  246.     //
  247.     // Get the infoType dictionary from the model
  248.     //
  249.     typeInfo = [ODBCAdaptor typeInfoForModel:model];
  250.     if(!typeInfo) return nil;;
  251.  
  252.     //
  253.     // Look everywhere, and return the best one. Or nil if not found
  254.     //
  255.     allKeys = [typeInfo allKeys];
  256.     for(i = 0, c = [allKeys count]; i < c; i++) {
  257.         NSString *key = [allKeys objectAtIndex:i];
  258.         NSDictionary *currentTypeInfo = [typeInfo objectForKey:key];
  259.         NSArray *defaultODBCType = [currentTypeInfo objectForKey:@"defaultODBCType"];
  260.         unsigned j,k;
  261.  
  262.         k = [defaultODBCType count];
  263.         if(k > position) k = position + 1;
  264.         for(j = 0; j < k ; j++) {
  265.             NSString *type = [defaultODBCType objectAtIndex:j];
  266.             if([type isEqualToString:odbcType]) {
  267.                 best = key;
  268.                 position = j;
  269.                 break;
  270.             }
  271.         }
  272.         // if(position == 0) break;
  273.     }
  274.     return best;
  275. }
  276.  
  277. + (void)assignExternalInfoForAttribute:(EOAttribute *)attribute
  278. {
  279.     int sqlTypeArray[20];
  280.     unsigned i = 0;
  281.     NSString *externalType = nil;
  282.     EOModel *model = [[attribute parent] model];
  283.     
  284.     //
  285.     // Set the column name from the attribute name
  286.     //
  287.     [super assignExternalInfoForAttribute:attribute];
  288.  
  289.     //
  290.     // Set the ODBC types we want to use prioritized.
  291.     //
  292.     switch ([attribute adaptorValueType]) {
  293.       case EOAdaptorNumberType:
  294.           if([attribute scale] != 0) {
  295.               sqlTypeArray[i++] = SQL_DECIMAL;
  296.               sqlTypeArray[i++] = SQL_NUMERIC;
  297.               sqlTypeArray[i++] = SQL_REAL;
  298.               sqlTypeArray[i++] = SQL_FLOAT;
  299.               sqlTypeArray[i++] = SQL_DOUBLE;
  300.           } else {
  301.               // sqlTypeArray[i++] = SQL_BIT; // Useless
  302.               // sqlTypeArray[i++] = SQL_TINYINT;
  303.               // sqlTypeArray[i++] = SQL_SMALLINT;
  304.               sqlTypeArray[i++] = SQL_INTEGER;
  305.               sqlTypeArray[i++] = SQL_BIGINT;
  306.               sqlTypeArray[i++] = SQL_DECIMAL;
  307.               sqlTypeArray[i++] = SQL_NUMERIC;
  308.               sqlTypeArray[i++] = SQL_REAL;
  309.               sqlTypeArray[i++] = SQL_FLOAT;
  310.               sqlTypeArray[i++] = SQL_DOUBLE;
  311.           }
  312.           break;
  313.  
  314.       case EOAdaptorDateType:
  315.           sqlTypeArray[i++] = SQL_TIMESTAMP;
  316.           // If there is no TIMESTAMP stuff, store it in a string
  317.           break;
  318.           
  319.       case EOAdaptorBytesType:
  320.           if([attribute width]) {
  321.               sqlTypeArray[i++] = SQL_BINARY;
  322.               sqlTypeArray[i++] = SQL_VARBINARY;
  323.               sqlTypeArray[i++] = SQL_LONGVARBINARY;
  324.           } else {
  325.               sqlTypeArray[i++] = SQL_LONGVARBINARY;
  326.               sqlTypeArray[i++] = SQL_VARBINARY;
  327.               sqlTypeArray[i++] = SQL_BINARY;
  328.           }
  329.           break;
  330.  
  331.       case EOAdaptorCharactersType:
  332.           if([attribute width]) {
  333.               sqlTypeArray[i++] = SQL_CHAR;
  334.               sqlTypeArray[i++] = SQL_VARCHAR;
  335.               sqlTypeArray[i++] = SQL_LONGVARCHAR;
  336.           } else {
  337.               sqlTypeArray[i++] = SQL_LONGVARCHAR;
  338.           }
  339.  
  340.           break;
  341.     }
  342.     // By default store it in a string. Since a driver must provide at least
  343.     // CHAR and VARCHAR, we are going to find at least one matching type.
  344.     sqlTypeArray[i++] = SQL_VARCHAR;
  345.     sqlTypeArray[i++] = SQL_CHAR;
  346.     sqlTypeArray[i++] = SQL_LONGVARCHAR;
  347.  
  348.     sqlTypeArray[i++] = SQL_TYPE_NULL;
  349.     
  350.     i = 0;
  351.     while(sqlTypeArray[i] != SQL_TYPE_NULL) {
  352.         NSDictionary *currentTypeInfo = nil;
  353.         
  354.         // Search an external type matching the ODBC type
  355.         externalType = [self externalTypeForOdbcType:sqlTypeArray[i] model:model];
  356.  
  357.         currentTypeInfo = [[ODBCAdaptor typeInfoForModel:model] objectForKey:externalType];
  358.  
  359.         if(externalType) {
  360.             // check precision and width and scales...
  361.             // Reject it if it does not fit
  362.             unsigned attPrecision;
  363.             int attScale;
  364.             NSString *typePrecision; // Precision or width
  365.             NSString *typeMaxScale;
  366.             NSString *typeMinScale;
  367.             BOOL found = NO;
  368.             
  369.             typePrecision = [currentTypeInfo objectForKey:@"precision"];
  370.             typeMaxScale = [currentTypeInfo objectForKey:@"maxScale"];
  371.             typeMinScale = [currentTypeInfo objectForKey:@"minScale"];
  372.             
  373.             // If precision is undefined, just accept the type as-is
  374.             if(!typePrecision) {
  375.                 found = YES; // Unlikely to happens
  376.             } else {
  377.                 switch ([attribute adaptorValueType]) {
  378.                     case EOAdaptorNumberType:
  379.                         attPrecision = [attribute precision];
  380.                         if(attPrecision > [typePrecision intValue]) {
  381.                             break; // Go to the next one
  382.                         }
  383.                         if(!typeMinScale || !typeMaxScale) {
  384.                             found = YES;
  385.                             break;
  386.                         }
  387.                         attScale = [attribute scale];
  388.                         if(attScale <= [typeMaxScale intValue] &&
  389.                            attScale >= [typeMinScale intValue]) {
  390.                             found = YES;
  391.                         }
  392.                         break;
  393.  
  394.                     case EOAdaptorCharactersType:
  395.                     case EOAdaptorBytesType:
  396.                         attPrecision = [attribute width];
  397.                         if(attPrecision <= [typePrecision intValue]) {
  398.                             found = YES;
  399.                         }
  400.                         break;
  401.  
  402.                     case EOAdaptorDateType:
  403.                         found = YES;
  404.                         break;
  405.                 }
  406.  
  407.             }
  408.  
  409.             if(found) break;
  410.         }
  411.         i++;
  412.     }
  413.     
  414.     [attribute setExternalType:externalType];
  415. }
  416.  
  417. + (void)assignExternalInfoForEntireModel:(EOModel *)model
  418. {
  419.     NS_DURING
  420.         [model setConnectionDictionary:[ODBCAdaptor resetOdbcInfoWithConnectionDictionary:[model connectionDictionary]]];
  421.         [super assignExternalInfoForEntireModel:model];
  422.     NS_HANDLER
  423.         NSLog(@"Unable to convert the model. If you retry it could succeed...\n%@", localException);
  424.     NS_ENDHANDLER
  425. }
  426.  
  427. @end
  428.  
  429. @implementation ODBCAdaptor(_ODBCPrivate)
  430.  
  431. // In this method there is one thing to do:
  432. // Check for the unsigned/signed crap.
  433.  
  434. - (EOAttribute *)attributeWithName:(NSString *)name columnName:(NSString *)columnName externalType:(NSString *)externalType odbcType:(int)odbcType length:(int)length precision:(int)precision scale:(int)scale nullable:(BOOL)nullable
  435. {
  436.     unsigned i = 0;
  437.     EOAttribute *attribute = [[[EOAttribute alloc] init] autorelease];
  438.     [attribute setName:name];
  439.     [attribute setColumnName:columnName];
  440.  
  441.     [attribute setExternalType:externalType];
  442.  
  443.     while(defaultCorrespondingTypes[i].odbcType != odbcType) {
  444.         i += 1;
  445.     }
  446.  
  447.     [attribute setValueClassName:defaultCorrespondingTypes[i].internalType];
  448.     [attribute setValueType:defaultCorrespondingTypes[i].valueType];
  449.  
  450.     switch([attribute adaptorValueType]) {
  451.         case EOAdaptorNumberType:
  452.             [attribute setPrecision:precision];
  453.             [attribute setScale:scale];
  454.             [attribute setWidth:0];
  455.             break;
  456.         case EOAdaptorDateType:
  457.             [attribute setWidth:0];
  458.             break;
  459.         case EOAdaptorCharactersType:
  460.         case EOAdaptorBytesType:
  461.             [attribute setWidth:length];
  462.             break;
  463.     }
  464.  
  465.     [attribute setAllowsNull:nullable];
  466.     return attribute;
  467. }
  468.  
  469. @end
  470.  
  471. @implementation ODBCAdaptor(ODBCInfoAccessors)
  472.  
  473. + (NSDictionary *)getOdbcInfoWithConnectionDictionary:(NSDictionary *)connectionDictionary
  474. {
  475.     NSAutoreleasePool *pool = [NSAutoreleasePool new];
  476.     ODBCAdaptor *adaptor = [[[self alloc] initWithName:@"ODBCAdaptor"] autorelease];
  477.     ODBCContext *context;
  478.     ODBCChannel *channel;
  479.     NSMutableDictionary *result;
  480.  
  481.     [adaptor setConnectionDictionary:connectionDictionary];
  482.     context = (ODBCContext *)[adaptor createAdaptorContext];
  483.     channel = (ODBCChannel *)[context createAdaptorChannel] ;
  484.  
  485.     result = [[connectionDictionary mutableCopy] autorelease];
  486.  
  487.     NS_DURING
  488.         [context odbcConnect];
  489.         [result setObject:[context odbcDriverInfo] forKey:driverInfoKey];
  490.         [result setObject:[channel odbcTypeInfo] forKey:typeInfoKey];
  491.         [context odbcDisconnect];
  492.     NS_HANDLER
  493.         // Setup two empty dictionaries in the result
  494.         if(![result objectForKey:driverInfoKey])
  495.             [result setObject:[NSDictionary dictionary] forKey:driverInfoKey];
  496.  
  497.         if(![result objectForKey:typeInfoKey])
  498.             [result setObject:[NSDictionary dictionary] forKey:typeInfoKey];
  499.  
  500.     NS_ENDHANDLER
  501.  
  502.     //
  503.     // Necessary because dealloc on the context need to access
  504.     // the driver info dictionary.
  505.     // We cannot use setConnectionDictionnary here, because it's raising
  506.     // if a connection is open...
  507.     //
  508.     [adaptor->_connectionDictionary autorelease];
  509.     adaptor->_connectionDictionary = [result retain];
  510.  
  511.     [result retain];
  512.     
  513.     [pool release]; // the adaptor/context/channel should all go away here.
  514.  
  515.     return [result autorelease];
  516. }
  517.  
  518. + (NSDictionary *)resetOdbcInfoWithConnectionDictionary:(NSDictionary *)connectionDictionary
  519. {
  520.     if([connectionDictionary objectForKey:driverInfoKey] || [connectionDictionary objectForKey:typeInfoKey]) {
  521.         NSMutableDictionary *newDict = [[connectionDictionary mutableCopy] autorelease];
  522.  
  523.         [newDict removeObjectForKey:driverInfoKey];
  524.         [newDict removeObjectForKey:typeInfoKey];
  525.         
  526.         return newDict;
  527.     } else {
  528.         return connectionDictionary;
  529.     }
  530. }
  531.  
  532. + (NSDictionary *)typeInfoForModel:(EOModel *)model
  533. {
  534.     NSDictionary *typeInfo;
  535.  
  536.     typeInfo = [[model connectionDictionary] objectForKey:typeInfoKey];
  537.  
  538.     if(!typeInfo) {
  539.         [model setConnectionDictionary:[ODBCAdaptor getOdbcInfoWithConnectionDictionary:[model connectionDictionary]]];
  540.         typeInfo = [[model connectionDictionary] objectForKey:typeInfoKey];
  541.     }
  542.     return typeInfo;
  543. }
  544.  
  545. + (NSDictionary *)driverInfoForModel:(EOModel *)model
  546. {
  547.     NSDictionary *driverInfo;
  548.  
  549.     driverInfo = [[model connectionDictionary] objectForKey:driverInfoKey];
  550.  
  551.     if(!driverInfo) {
  552.         [model setConnectionDictionary:[ODBCAdaptor getOdbcInfoWithConnectionDictionary:[model connectionDictionary]]];
  553.         driverInfo = [[model connectionDictionary] objectForKey:driverInfoKey];
  554.     }
  555.     return driverInfo;
  556. }
  557.  
  558. - (NSDictionary *)typeInfo
  559. {
  560.     NSDictionary *typeInfo = [_connectionDictionary objectForKey:typeInfoKey];
  561.  
  562.     if(!typeInfo) {
  563.         // I cannot use the accessor -setConnectionDictionary: because
  564.         // there is a raise if a channel is open...
  565.         //
  566.         _connectionDictionary = [[ODBCAdaptor getOdbcInfoWithConnectionDictionary:[_connectionDictionary autorelease]] retain];
  567.         typeInfo = [_connectionDictionary objectForKey:typeInfoKey];
  568.     }
  569.     return typeInfo;
  570. }
  571.  
  572. - (NSDictionary *)driverInfo
  573. {
  574.     NSDictionary *driverInfo = [_connectionDictionary objectForKey:driverInfoKey];
  575.  
  576.     if(!driverInfo) {
  577.         // I cannot use the accessor -setConnectionDictionary: because
  578.         // there is a raise if a channel is open...
  579.         //
  580.         _connectionDictionary = [[ODBCAdaptor getOdbcInfoWithConnectionDictionary:[_connectionDictionary autorelease]] retain];
  581.         driverInfo = [_connectionDictionary objectForKey:driverInfoKey];
  582.     }
  583.     return driverInfo;
  584. }
  585.  
  586. @end
  587.