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

  1. /*
  2.    ODBCChannel.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. @implementation ODBCChannel
  22.  
  23. - (void)_beginFetch
  24. {
  25.     [(ODBCContext *)_context channelWillBeginFetching];
  26.     _flags.fetchInProgress = 1;
  27.     _rowsProcessedCount = 0;
  28. }
  29.  
  30. - (void)_endFetch
  31. {
  32.  
  33.     if (_flags.fetchInProgress) {
  34.         RETCODE result;
  35.  
  36.         if (_debug) NSLog (@" *** %@ %d rows processed\n", self, _rowsProcessedCount);
  37.         _flags.fetchInProgress = NO;
  38.  
  39.         // Discard cursor and results
  40.         result = SQLFreeStmt (_statement, SQL_UNBIND);
  41.         if ( result != SQL_SUCCESS ) {
  42.             [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFreeStmt in -[ODBCChannel _endFetch]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  43.         }
  44.  
  45.         result = SQLFreeStmt (_statement, SQL_CLOSE);
  46.         if ( result != SQL_SUCCESS ) {
  47.             [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFreeStmt in -[ODBCChannel _endFetch]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  48.         }
  49.  
  50.         [(ODBCContext *)_context channelDidEndFetching];
  51.  
  52.         if(_flags.beganTransaction) {
  53.             [_context commitTransaction];
  54.             _flags.beganTransaction = NO;
  55.         }
  56.         
  57.     }
  58.     [self setAttributesToFetch:nil];
  59. }
  60.  
  61. - initWithAdaptorContext:(EOAdaptorContext *)context
  62. {
  63.     [super initWithAdaptorContext:context];
  64.     return self;
  65. }
  66.  
  67. - (void)dealloc
  68. {
  69.     // If statement is active then close channel
  70.     if (_statement) [self closeChannel];
  71.  
  72.     // release attributes and columns
  73.     [_attributes release];
  74.     [_selectedColumns release];
  75.     [super dealloc];
  76. }
  77.  
  78. - (BOOL)isOpen
  79. {
  80.     return (_statement) ? YES : NO;
  81. }
  82.  
  83. - (void)openChannel
  84. {
  85.     RETCODE result;
  86.     HDBC    connection;
  87.     HSTMT   hStmt = 0;
  88.  
  89.     //
  90.     // If already opened then return
  91.     //
  92.     if ([self isOpen]) return;
  93.  
  94.     //
  95.     // Get the connection from the context
  96.     //
  97.     [(ODBCContext *)_context channelWillOpen];
  98.     connection = [(ODBCContext *)_context odbcDatabaseConnection];
  99.  
  100.     //
  101.     // If not connected then raise exception
  102.     //
  103.     if (!connection) {
  104.         [NSException raise:EOGeneralAdaptorException format:@"%@ -- %@ 0x%x: Unable to open channel.  Unable to logon.", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  105.     }
  106.  
  107.     //
  108.     // Allocate statement with connection
  109.     //
  110.     result = SQLAllocStmt (connection, &hStmt);
  111.  
  112.     if ( result != SQL_SUCCESS ) {
  113.         if(result != SQL_SUCCESS_WITH_INFO) {
  114.             [(ODBCContext *)_context channelDidClose];
  115.         }
  116.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLAllocStmt in -[ODBCChannel openChannel]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  117.     }
  118.  
  119.     _statement = hStmt;
  120. }
  121.  
  122. - (void)closeChannel
  123. {
  124.     RETCODE result;
  125.  
  126.     //
  127.     // If not statement allocated then return
  128.     //
  129.     if (!_statement) return;
  130.  
  131.     //
  132.     // If we are fetching then cancel fetch
  133.     //
  134.     if (_flags.fetchInProgress) [self cancelFetch];
  135.  
  136.     //
  137.     // Free statement by dropping all resorces
  138.     //
  139.     result = SQLFreeStmt (_statement, SQL_DROP);
  140.     _statement = 0;
  141.  
  142.     if ( result != SQL_SUCCESS ) {
  143.         if(result != SQL_SUCCESS_WITH_INFO) {
  144.             [(ODBCContext *)_context channelDidClose];
  145.         }
  146.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFreeStmt in -[ODBCChannel closeChannel]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  147.     }
  148.  
  149.     // notify of channel closure
  150.     [(ODBCContext *)_context channelDidClose];
  151. }
  152.  
  153. - (void)insertRow:(NSDictionary *)row forEntity:(EOEntity *)entity
  154. {
  155.     [self evaluateExpression:[[[_context adaptor] expressionClass] insertStatementForRow:row entity:entity]];
  156. }
  157.  
  158. - (unsigned)updateValues:(NSDictionary *)row inRowsDescribedByQualifier:(EOQualifier *)qualifier entity:(EOEntity *)entity
  159. {
  160.     [self evaluateExpression:[[[_context adaptor] expressionClass] updateStatementForRow:row qualifier:qualifier entity:entity]];
  161.  
  162.     return _rowsProcessedCount;
  163. }
  164.  
  165. - (unsigned)deleteRowsDescribedByQualifier:(EOQualifier *)qualifier entity:(EOEntity *)entity
  166. {
  167.     [self evaluateExpression:[[[_context adaptor] expressionClass] deleteStatementWithQualifier:qualifier entity:entity]];
  168.  
  169.     return _rowsProcessedCount;
  170. }
  171.  
  172. - (void)selectAttributes:(NSArray *)attributes fetchSpecification:(EOFetchSpecification *)fetchSpec lock:(BOOL)yn entity:(EOEntity *)entity
  173. {
  174.     NSString *selectString;
  175.     EOSQLExpression *sqlExpression;
  176.  
  177.     if (_delegateRespondsTo.shouldSelectAttributes) {
  178.         if (![_delegate adaptorChannel:self shouldSelectAttributes:attributes fetchSpecification:[[fetchSpec copy] autorelease] lock:yn entity:entity]) {
  179.             return;
  180.         }
  181.     }
  182.  
  183.     selectString = [entity externalQuery];
  184.  
  185.     if (!selectString || [fetchSpec qualifier]) {
  186.         sqlExpression = [[[_context adaptor] expressionClass] selectStatementForAttributes:attributes lock:yn fetchSpecification:fetchSpec entity:entity];
  187.     } else {
  188.         sqlExpression = [[[_context adaptor] expressionClass] expressionForString:selectString];
  189.     }
  190.  
  191.     [self evaluateExpression:sqlExpression];
  192.  
  193.     [self setAttributesToFetch:attributes];
  194.  
  195.     if (_delegateRespondsTo.didSelectAttributes)
  196.         [_delegate adaptorChannel:self didSelectAttributes:attributes fetchSpecification:fetchSpec lock:yn entity:entity];
  197.  
  198.     return;
  199. }
  200.  
  201. - (void)_bindInputVariablesWithBindings:(NSArray *)bindings
  202. {
  203.     unsigned i,c;
  204.     RETCODE result;
  205.  
  206.     result = SQLFreeStmt (_statement, SQL_RESET_PARAMS);
  207.     if ( result != SQL_SUCCESS ) {
  208.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFreeStmt in -[ODBCChannel _bindInputVariablesWithBindings:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  209.     }
  210.  
  211.     for(i = 0,c = [bindings count]; i < c; i++) {
  212.         NSMutableDictionary *bindDict = [bindings objectAtIndex:i];
  213.         EOAttribute *att = [bindDict objectForKey:EOBindVariableAttributeKey];
  214.         ODBCColumn *col = [[ODBCColumn alloc] initWithAttribute:att channel:self];
  215.  
  216.         [bindDict setObject:col forKey:EOBindVariableColumnKey];
  217.         [col release];
  218.  
  219.         [col bindAttribute:att forInputColumn:(i+1) ofStatement:_statement];
  220.         [col takeInputValue:[bindDict objectForKey:EOBindVariableValueKey]];
  221.     }
  222. }
  223.  
  224. - (void)evaluateExpression:(EOSQLExpression *)expression
  225. {
  226.     RETCODE result;
  227.     NSStringEncoding encoding = [[_context adaptor] databaseEncoding];
  228.     NSData *data;
  229.     NSString *sqlString;
  230.  
  231.     if (_flags.fetchInProgress) {
  232.         [NSException raise:NSInternalInconsistencyException format:@"%@ -- %@ 0x%x: illegal attempt to evaluateExpression while a fetch is in progress", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  233.     }
  234.  
  235.     if (_delegateRespondsTo.shouldEvaluateExpression) {
  236.         if (![_delegate adaptorChannel:self shouldEvaluateExpression:expression]) return;
  237.     }
  238.  
  239.     sqlString = [expression statement];
  240.     data = [sqlString dataUsingEncoding:encoding];
  241.  
  242.     // If there are characters in the string that can't be converted,
  243.     // then the data will be nil, call report error and return FAIL.
  244.     if (!data) {
  245.         [NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: attempt to evaluate SQL statement that contains characters that can not be converted to target database encoding: %@", NSStringFromSelector(_cmd), NSStringFromClass([self class]), sqlString];
  246.     }
  247.  
  248.     if (_debug) NSLog (@" *** [%@ evaluateExpression: %@]\n", self, expression);
  249.  
  250.     // Bind the variables
  251.     [self _bindInputVariablesWithBindings:[expression bindVariableDictionaries]];
  252.  
  253.     //
  254.     // We are not reusing bindings. Just drop them.
  255.     //
  256.     result = SQLFreeStmt (_statement, SQL_UNBIND);
  257.     if ( result != SQL_SUCCESS ) {
  258.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFreeStmt in -[ODBCChannel evaluateExpression:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  259.     }
  260.  
  261.     if (![_context transactionNestingLevel]) {
  262.         [_context beginTransaction];
  263.         _flags.beganTransaction = YES;
  264.     } else {
  265.         _flags.beganTransaction = NO;
  266.     }
  267.  
  268.     _rowsProcessedCount = 0;
  269.  
  270.     // Execute SQL statement
  271.     result = SQLExecDirect(_statement, (UCHAR*)[data bytes], SQL_NTS);
  272.  
  273.     if ( result != SQL_SUCCESS ) {
  274.         if(result != SQL_SUCCESS_WITH_INFO) {
  275.             if(_flags.beganTransaction) {
  276.                 [_context rollbackTransaction];
  277.                 _flags.beganTransaction = NO;
  278.             }
  279.         }
  280.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLExecDirect in -[ODBCChannel evaluateExpression:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  281.     }
  282.  
  283.     // If the SQL just executed was a select, mark the channel as
  284.     // inFetchMode. The first call to fetchWithAttributes:withZone: will
  285.     // actually do the describe (if needed), the define, execute, and fetch.
  286.     //
  287.     if ([sqlString hasPrefix: @"SELECT"])
  288.         [self _beginFetch];
  289.  
  290.     if([sqlString hasPrefix:@"UPDATE"] || [sqlString hasPrefix:@"DELETE"] || [sqlString hasPrefix:@"INSERT"]) {
  291.         SDWORD rowCount;
  292.         result = SQLRowCount(_statement, &rowCount);
  293.  
  294.         if ( result != SQL_SUCCESS ) {
  295.             if(result != SQL_SUCCESS_WITH_INFO) {
  296.                 if(_flags.beganTransaction) {
  297.                     [_context rollbackTransaction];
  298.                     _flags.beganTransaction = NO;
  299.                 }
  300.             }
  301.             [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLRowCount in -[ODBCChannel evaluateExpression:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  302.         }
  303.  
  304.         _rowsProcessedCount = rowCount;
  305.         if (_debug) NSLog (@" *** %@ %d rows processed\n", self, (int)_rowsProcessedCount);
  306.     }
  307.  
  308.     if (_delegateRespondsTo.didEvaluateExpression)
  309.         [_delegate adaptorChannel:self didEvaluateExpression:expression];
  310.  
  311.     if(!_flags.fetchInProgress && _flags.beganTransaction) {
  312.         [_context commitTransaction];
  313.         _flags.beganTransaction = NO;
  314.     }
  315.  
  316.     return;
  317. }
  318.  
  319. - (BOOL)isFetchInProgress
  320. {
  321.     return _flags.fetchInProgress;
  322. }
  323.  
  324. - (NSArray *)describeResults
  325. {
  326.     NSMutableArray *attributes  = [NSMutableArray array];
  327.     RETCODE result;
  328.     SWORD column, columnCount;
  329.  
  330.     // Get number of columns in result set
  331.     result = SQLNumResultCols (_statement, &columnCount);
  332.  
  333.     if ( result != SQL_SUCCESS ) {
  334.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLNumResultCols in -[ODBCChannel describeResults]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  335.     }
  336.  
  337.     // Create and attribute for each column in result set
  338.     for (column = 1; column <= columnCount; column++) {
  339.         EOAttribute *att;
  340.         static UCHAR columnName[SQL_MAX_MESSAGE_LENGTH]; // Column name
  341.         SWORD columnNameLength, sqlType, scale, isNullable;
  342.         UDWORD colDef;
  343.         RETCODE result;
  344.         NSString* name;
  345.  
  346.         // Get column description
  347.         if ((result = SQLDescribeCol (_statement, column, columnName, sizeof(columnName), &columnNameLength, &sqlType, &colDef, &scale, &isNullable)) != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO)
  348.             break;
  349.  
  350.         name = [[NSString stringWithCString:columnName length:columnNameLength] uppercaseString];
  351.         att = [(ODBCAdaptor*)[_context adaptor] attributeWithName:name columnName:name externalType:@"" /* unknown type */ odbcType:sqlType length:colDef precision:colDef scale:scale nullable:(isNullable == SQL_NULLABLE) ? YES : NO];
  352.  
  353.             [attributes addObject:att];
  354.     };
  355.  
  356.     return attributes;
  357. }
  358.  
  359. - (NSMutableDictionary *)fetchRowWithZone:(NSZone *)zone
  360. {
  361.     RETCODE result;
  362.     NSMutableDictionary *row = nil;
  363.  
  364.     if (![self isFetchInProgress])
  365.         return nil; // Should be an error ?
  366.  
  367.     // Check if it's the first row, and in this case
  368.     // verify that the attribute set is here
  369.     if(!_rowsProcessedCount) {
  370.         if(!_attributes) {
  371.             [self setAttributesToFetch:[self describeResults]];
  372.         }
  373.     }
  374.  
  375.     if (_delegateRespondsTo.willFetchRow) {
  376.         [_delegate adaptorChannelWillFetchRow:self];
  377.     }
  378.  
  379.     result = SQLFetch (_statement);
  380.  
  381.     if(result == SQL_NO_DATA_FOUND) {
  382.         [self _endFetch];
  383.         return nil;
  384.     }
  385.  
  386.     if ( result != SQL_SUCCESS ) {
  387.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLFetch in -[ODBCChannel fetchRowWithZone:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  388.     }
  389.  
  390.     {
  391.         int i, columnCount;
  392.         id valueBuffer[100];
  393.         id *values, *sp, *ep;
  394.  
  395.         // Create array for values if 100 is too small
  396.         columnCount = [_selectedColumns count];
  397.         values = (columnCount > 100) ? valueBuffer : (id *)NSZoneMalloc(NULL, columnCount*sizeof(id));
  398.  
  399.         // Fetch values for row
  400.         for (i = 0; i < columnCount; i++)
  401.             values[i] = [[_selectedColumns objectAtIndex:i] fetchWithZone : zone];
  402.  
  403.         // construct a dictionary with the values
  404.         if ((row = [self dictionaryWithObjects:values forAttributes:_attributes zone:zone]))
  405.             _rowsProcessedCount++;
  406.  
  407.         // release all of the objects
  408.         for(sp = values, ep = sp + columnCount; sp < ep; sp++)
  409.             [*sp release];
  410.  
  411.         // relase values if we allocated a value buffer
  412.         if (values != valueBuffer) NSZoneFree(NULL, values);
  413.  
  414.     }    
  415.  
  416.     if(row && _delegateRespondsTo.didFetchRow) {
  417.         [_delegate adaptorChannel:self didFetchRow:row];
  418.     }
  419.  
  420.     return row;
  421. }
  422.  
  423. - (void)setAttributesToFetch:(NSArray *)attributes
  424. {
  425.     int i, count = 0;
  426.     BOOL bind = YES;
  427.     
  428.     [_attributes autorelease];
  429.     _attributes = [attributes retain];
  430.  
  431.     count = [_attributes count];
  432.     if(_selectedColumns) {
  433.         [_selectedColumns removeAllObjects];
  434.     } else if(count) {
  435.         _selectedColumns = [[NSMutableArray alloc] initWithCapacity:count];
  436.     }
  437.  
  438.     // Create ODBCColumns and define fetch buffers for each of the attributes
  439.     // in the select list.
  440.     for (i = 0; i < count; i++) {
  441.         EOAttribute *attribute;
  442.         ODBCColumn *column;
  443.  
  444.         // Create column for selected attribute
  445.         attribute = [_attributes objectAtIndex:i];
  446.         column = [[ODBCColumn alloc] initWithAttribute:attribute channel:self];
  447.         [_selectedColumns addObject: column];
  448.         [column release];
  449.  
  450.         if(![column couldBind]) {
  451.             bind = NO;
  452.         }
  453.         // bind column (columns start at 1)
  454.         [column connectToColumn:(i+1) ofStatement:_statement useBinding:bind];
  455.     }
  456. }
  457.  
  458. - (NSArray *)attributesToFetch
  459. {
  460.     return _attributes;
  461. }
  462.  
  463. - (void)cancelFetch
  464. {
  465.     if(_flags.fetchInProgress) {
  466.         if (_debug) NSLog (@" !!! %@ fetch canceled\n", self);
  467.         [self _endFetch];
  468.     }
  469. }
  470.  
  471. - (void *)odbcStatement
  472. {
  473.     return _statement;
  474. }
  475.  
  476. - (NSDictionary *)odbcTypeInfo
  477. {
  478.     NSMutableDictionary *typeInfo = [NSMutableDictionary new];
  479.     RETCODE result;
  480.     NSDictionary *row;
  481.     BOOL autoOpening = NO;
  482.  
  483.     if (_debug) NSLog (@" *** [%@ odbcTypeInfo]\n", self);
  484.  
  485.     if(![self isOpen]) {
  486.         autoOpening = YES;
  487.         [self openChannel];
  488.     }
  489.  
  490.     // Execute SQL statement
  491.     result = SQLGetTypeInfo(_statement, SQL_ALL_TYPES);
  492.  
  493.     if ( result != SQL_SUCCESS ) {
  494.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLGetTypeInfo in -[ODBCChannel odbcTypeInfo]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  495.     }
  496.  
  497.     //
  498.     // Do we need to disable the delegate here ?
  499.     //
  500.     [self _beginFetch];
  501.  
  502.     while(row = [self fetchRowWithZone:NULL]) {
  503.         NSString *typeName = [row objectForKey:@"TYPE_NAME"];
  504.         NSMutableDictionary *local;
  505.         id value;
  506.  
  507.         if(local = [typeInfo objectForKey:typeName]) {
  508.             value = [ODBCAdaptor stringRepresentationForOdbcType:[[row objectForKey:@"DATA_TYPE"] intValue]];
  509.             [[local objectForKey:@"defaultODBCType"] addObject:value];
  510.         } else {
  511.  
  512.             value = [row objectForKey:@"AUTO_INCREMENT"];
  513.             if((value != [EONull null]) && [value intValue]) {
  514.                 continue; // Yuck, we do not support those.
  515.             }
  516.             
  517.             local = [NSMutableDictionary dictionary];
  518.             // Extract the datatype
  519.             value = [ODBCAdaptor stringRepresentationForOdbcType:[[row objectForKey:@"DATA_TYPE"] intValue]];
  520.             [local setObject:[NSMutableArray arrayWithObject:value] forKey:@"defaultODBCType"];
  521.             // Extract the precision
  522.             value = [row objectForKey:@"PRECISION"];
  523.             if(value != [EONull null]) {
  524.                 [local setObject:[value stringValue] forKey:@"precision"];
  525.             }
  526.             // Extract the minscale
  527.             value = [row objectForKey:@"MINIMUM_SCALE"];
  528.             if(value != [EONull null]) {
  529.                 [local setObject:[value stringValue] forKey:@"minScale"];
  530.             }
  531.             // Extract the maxscale
  532.             value = [row objectForKey:@"MAXIMUM_SCALE"];
  533.             if(value != [EONull null]) {
  534.                 [local setObject:[value stringValue] forKey:@"maxScale"];
  535.             }
  536.             // Extract the unsigned status
  537.             value = [row objectForKey:@"UNSIGNED_ATTRIBUTES"];
  538.             if(value != [EONull null]) {
  539.                 [local setObject:[value intValue] ? @"YES":@"NO" forKey:@"isUnsigned"];
  540.             }
  541.             // Extract the nullable status
  542.             value = [row objectForKey:@"NULLABLE"];
  543.             if(value != [EONull null]) {
  544.                 [local setObject:[value intValue] ? @"YES":@"NO" forKey:@"isNullable"];
  545.             }
  546.             // Extract the searcheable status
  547.             value = [row objectForKey:@"SEARCHABLE"];
  548.             if(value != [EONull null]) {
  549.                 [local setObject:(([value intValue] == SQL_SEARCHABLE) || ([value intValue] == SQL_ALL_EXCEPT_LIKE)) ? @"YES":@"NO" forKey:@"isSearchable"];
  550.             }
  551.             // Extract the number of args in the type definition
  552.             value = [row objectForKey:@"CREATE_PARAMS"];
  553.             if(value != [EONull null]) {
  554.                 NSArray *array = [value componentsSeparatedByString:@","];
  555.                 [local setObject:[NSString stringWithFormat:@"%d",[array count]] forKey:@"createParams"];
  556.             } else {
  557.                 [local setObject:@"0" forKey:@"createParams"];
  558.             }
  559.             [typeInfo setObject:local forKey:typeName];
  560.         }
  561.     }
  562.     
  563.     [self _endFetch];
  564.  
  565.     if(autoOpening) {
  566.         [self closeChannel];
  567.     }
  568.     return typeInfo;
  569. }
  570.  
  571. - (NSArray *)_entityGroupForEntity:(EOEntity *)entity
  572. {
  573.     NSMutableArray *result = [NSMutableArray array];
  574.     NSMutableArray *tableNames = [NSMutableArray array];
  575.     NSArray *subEntities = [entity subEntities];
  576.  
  577.     if(![entity isAbstractEntity] && [entity externalName]) {
  578.         [result addObject:entity];
  579.         [tableNames addObject:[entity externalName]];
  580.     }
  581.  
  582.     if(subEntities) {
  583.         unsigned i,c;
  584.  
  585.         for(i = 0, c = [subEntities count]; i < c; i++) {
  586.             EOEntity *sub = [subEntities objectAtIndex:i];
  587.             NSArray *merge = [self _entityGroupForEntity:sub];
  588.             unsigned j,k;
  589.  
  590.             for(j = 0, k = [merge count]; j < k; j++) {
  591.                 unsigned index;
  592.                 EOEntity *ent = [merge objectAtIndex:j];
  593.  
  594.                 index = [tableNames indexOfObject:[ent externalName]];
  595.                 if(index == NSNotFound) {
  596.                     [result addObject:ent];
  597.                     [tableNames addObject:[ent externalName]];
  598.  
  599.                 }
  600.             }
  601.  
  602.         }
  603.  
  604.     }
  605.     return result;
  606. }
  607.  
  608. - (NSDictionary *)primaryKeyForNewRowWithEntity:(EOEntity *)entity
  609. {
  610.     NSArray *primaryKeyAttributes = [entity primaryKeyAttributes];
  611.     EOAttribute *primAttribute;
  612.     Class expressionClass;
  613.     EOSQLExpression *getRowExpr;
  614.     EOSQLExpression *updateRowExpr;
  615.     NSDictionary *row = nil;
  616.     NSNumber *returnedValue, *newValue = nil;
  617.     BOOL needToCreateTable = NO;
  618.  
  619.     if([primaryKeyAttributes count] != 1) {
  620.         return nil; // We support only simple primary keys
  621.     }
  622.  
  623.     primAttribute = [primaryKeyAttributes objectAtIndex:0];
  624.  
  625.     if([primAttribute adaptorValueType] != EOAdaptorNumberType) {
  626.         return nil; // We support only number keys
  627.     }
  628.  
  629.     expressionClass = [[[self adaptorContext] adaptor] expressionClass];
  630.     getRowExpr = [expressionClass expressionForString:[NSString stringWithFormat:@"SELECT PK FROM EO_PK_TABLE WHERE NAME = '%@'", [entity name]]];
  631.  
  632.     //[[self adaptorContext] beginTransaction];
  633.     NS_DURING
  634.         //
  635.         // Fetch the row
  636.         //
  637.         [self evaluateExpression:getRowExpr];
  638.         row = [self fetchRowWithZone:NULL]; // fetch one row
  639.         [self cancelFetch];
  640.     NS_HANDLER
  641.         [self cancelFetch];
  642.         needToCreateTable = YES;
  643.     NS_ENDHANDLER
  644.  
  645.     if(needToCreateTable) {
  646.         EOSQLExpression *createTableExpr;
  647.         NSString *textType;
  648.  
  649.         textType = [ODBCAdaptor externalTypeForOdbcType:SQL_CHAR model:[entity model]];
  650.  
  651.         createTableExpr = [expressionClass expressionForString:[NSString stringWithFormat:@"CREATE TABLE EO_PK_TABLE ( NAME %@(40), PK %@ )", textType, [getRowExpr columnTypeStringForAttribute:primAttribute]]];
  652.  
  653.         NS_DURING
  654.             //
  655.             // Create the table
  656.             //
  657.             [self evaluateExpression:createTableExpr];
  658.         NS_HANDLER
  659.             //[[self adaptorContext] rollbackTransaction];
  660.             [localException raise];
  661.         NS_ENDHANDLER
  662.     }
  663.  
  664.     if(row) {
  665.         returnedValue = [row objectForKey:@"PK"];
  666.     } else {
  667.         EOSQLExpression *insertRowExpr;
  668.  
  669.         insertRowExpr = [expressionClass expressionForString:[NSString stringWithFormat:@"INSERT INTO EO_PK_TABLE VALUES ( '%@', 0 )", [entity name]]];
  670.  
  671.         NS_DURING
  672.             //
  673.             // Insert a row with 0
  674.             //
  675.             [self evaluateExpression:insertRowExpr];
  676.         NS_HANDLER
  677.             //[[self adaptorContext] rollbackTransaction];
  678.             [localException raise];
  679.         NS_ENDHANDLER
  680.  
  681.         returnedValue = [NSNumber numberWithInt:0];
  682.     }
  683.  
  684.  
  685.     if((id)returnedValue == [EONull null] || ![returnedValue intValue]) {
  686.         // Find the current maximum, and set it as the newValue...
  687.         NSArray *entityGroup = [self _entityGroupForEntity:entity];
  688.         unsigned i,c;
  689.         
  690.         newValue = [NSNumber numberWithInt:0];
  691.  
  692.         for(i = 0, c = [entityGroup count]; i < c; i++) {
  693.             EOEntity *currentEntity = [entityGroup objectAtIndex:i];
  694.             EOAttribute *att = [currentEntity attributeNamed:[primAttribute name]];
  695.             NSDictionary *r;
  696.             NSNumber *tempValue;
  697.             EOSQLExpression *searchExpr = [expressionClass expressionForString:[NSString stringWithFormat:@"SELECT MAX(%@) FROM %@", [att columnName], [currentEntity externalName]]];
  698.  
  699.             NS_DURING
  700.                 //
  701.                 // Fetch the row
  702.                 //
  703.                 [self evaluateExpression:searchExpr];
  704.                 [self setAttributesToFetch:[NSArray arrayWithObject:att]];
  705.                 r = [self fetchRowWithZone:NULL]; // fetch one row
  706.                 [self cancelFetch];
  707.             NS_HANDLER
  708.                 [self cancelFetch];
  709.                 [localException raise];
  710.             NS_ENDHANDLER
  711.  
  712.             tempValue = [r objectForKey:[att name]];
  713.             if(tempValue && ((id)tempValue != [EONull null]) && ([tempValue compare:newValue] == NSOrderedDescending))
  714.                 newValue = tempValue;
  715.         }
  716.     } else {
  717.         newValue = returnedValue;
  718.     }
  719.     newValue = [NSNumber numberWithInt:[newValue intValue] + 1];
  720.  
  721.     updateRowExpr = [expressionClass expressionForString:[NSString stringWithFormat:@"UPDATE EO_PK_TABLE SET PK = %d WHERE NAME = '%@' AND PK = %d", [newValue intValue], [entity name], [returnedValue intValue]]];
  722.  
  723.     NS_DURING
  724.         //
  725.         // Update the table
  726.         //
  727.         [self evaluateExpression:updateRowExpr];
  728.     NS_HANDLER
  729.         //[[self adaptorContext] rollbackTransaction];
  730.         [localException raise];
  731.     NS_ENDHANDLER
  732.  
  733.     if(_rowsProcessedCount == 1) {
  734.         ;//[[self adaptorContext] commitTransaction];
  735.     } else {
  736.         // Somebody did ask for a key at the same time !
  737.         // Redo it ?
  738.         //[[self adaptorContext] rollbackTransaction];
  739.         return nil;
  740.     }
  741.  
  742.     return [NSDictionary dictionaryWithObjectsAndKeys:newValue, [primAttribute name], nil];
  743. }
  744.  
  745.  
  746.  
  747. // Table description
  748. - (NSArray *)describeTableNames
  749.     // Reads and returns an array of table names from the database.  This
  750.     // method in conjunction with describeModelWithTableNames: is used for
  751.     // building a default model in EOModeler. This methods may raise an
  752.     // exception if an error occurs
  753. {
  754.     RETCODE result;
  755.     NSDictionary *row;
  756.     NSMutableArray *tables = [NSMutableArray array];
  757.  
  758.     if (_debug) NSLog (@" *** [%@ describeTableNames]\n", self);
  759.  
  760.     // Execute SQL statement
  761.     result = SQLTables(_statement, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
  762.  
  763.     if ( result != SQL_SUCCESS ) {
  764.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLTables in -[ODBCChannel describeTableNames]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  765.     }
  766.  
  767.     //
  768.     // Do we need to disable the delegate here ?
  769.     //
  770.     [self _beginFetch];
  771.  
  772.     while(row = [self fetchRowWithZone:NULL]) {
  773.         NSString *tableName = [row objectForKey:@"TABLE_NAME"];
  774.         [tables addObject:tableName];
  775.     }
  776.  
  777.     [self _endFetch];
  778.  
  779.     return tables;
  780. }
  781.  
  782. - (void)_getAttributesForEntity:(EOEntity *)entity
  783. {
  784.     RETCODE result;
  785.     NSDictionary *row;
  786.     const char *cStringTableName = [[entity name] cString];
  787.     
  788.     if (_debug) NSLog (@" *** [%@ describeTable:%s]\n", self, cStringTableName);
  789.  
  790.     result = SQLColumns(_statement, NULL, 0, NULL, 0, (char *)cStringTableName, strlen(cStringTableName), NULL, 0);
  791.  
  792.     if ( result != SQL_SUCCESS ) {
  793.         [(ODBCContext *)_context odbcErrorWithChannel:self string:@"SQLColumns in -[ODBCChannel _getAttributesForEntity:]" raise: (result != SQL_SUCCESS_WITH_INFO)];
  794.     }
  795.  
  796.     //
  797.     // Do we need to disable the delegate here ?
  798.     //
  799.     [self _beginFetch];
  800.  
  801.     while(row = [self fetchRowWithZone:NULL]) {
  802.         id value;
  803.         EOAttribute *attribute;
  804.         NSString *columnName = [row objectForKey:@"COLUMN_NAME"];
  805.         NSString *externalType = [row objectForKey:@"TYPE_NAME"];
  806.         int odbcType = [[row objectForKey:@"DATA_TYPE"] intValue];
  807.         int precision, scale, length;
  808.         BOOL nullable = YES;
  809.         
  810.         value = [row objectForKey:@"PRECISION"];
  811.         if(value == [EONull null])
  812.             precision = 0;
  813.         else
  814.             precision = [value intValue];
  815.  
  816.         value = [row objectForKey:@"SCALE"];
  817.         if(value == [EONull null])
  818.             scale = 0;
  819.         else
  820.             scale = [value intValue];
  821.  
  822.         value = [row objectForKey:@"SCALE"];
  823.         if(value == [EONull null])
  824.             scale = 0;
  825.         else
  826.             scale = [value intValue];
  827.  
  828.         value = [row objectForKey:@"LENGTH"];
  829.         if(value == [EONull null])
  830.             length = 0;
  831.         else
  832.             length = [value intValue];
  833.  
  834.         if([[row objectForKey:@"NULLABLE"] intValue] == SQL_NO_NULLS)
  835.             nullable = NO;
  836.  
  837.         attribute = [(ODBCAdaptor *)[_context adaptor] attributeWithName:columnName columnName:columnName externalType:externalType odbcType:odbcType length:length precision:precision scale:scale nullable:nullable];
  838.  
  839.         [entity addAttribute:attribute];
  840.     }
  841.  
  842.     [self _endFetch];
  843.  
  844. }
  845.  
  846.  
  847. - (EOModel *)describeModelWithTableNames:(NSArray *)tableNames
  848.     // Constructs a default model out of the database's meta data.  It also
  849.     // put the adaptor name and connection dictionary in the new model.
  850.     // This methods may raise an exception if an error occurs.
  851. {
  852.     EOModel *model = [[EOModel new] autorelease];
  853.     EOAdaptor *adaptor = [[self adaptorContext] adaptor];
  854.     unsigned i,c;
  855.     
  856.     [model setAdaptorName:[adaptor name]];
  857.     [model setConnectionDictionary:[adaptor connectionDictionary]];
  858.  
  859.     for(i=0, c=[tableNames count]; i < c; i++) {
  860.         NSString *table = [tableNames objectAtIndex:i];
  861.  
  862.         EOEntity *entity;
  863.  
  864.         entity = [[EOEntity allocWithZone:[model zone]] init];
  865.         [entity setName:table];
  866.         [entity setExternalName:table];
  867.         [model addEntity:entity];
  868.         [entity release];
  869.  
  870.         [self _getAttributesForEntity:entity];
  871.         
  872.     }
  873.     
  874.     return model;
  875. }
  876.  
  877. @end
  878.  
  879. @implementation ODBCChannel(EOStoredProcedures)
  880.  
  881. #if 0
  882. #warning TODO: Stored proc support
  883. - (void)describeStoredProceduresForModel:(EOModel *)model;
  884.     // This method may raise an exception if an error occcurs.
  885.  
  886.  
  887. - (void)executeStoredProcedure:(EOStoredProcedure *)storedProcedure withValues:(NSDictionary *)values
  888. {
  889.     _EORequestConcreteImplementation (self, _cmd, [EOAdaptorChannel class]);
  890. }
  891. #endif
  892. @end
  893.