I have taken the time to compile a list of bugs in the VisualAge 6.0.3/7.0 Oracle Database Framework that I have encountered over time. In the cases where I found a solution, I have included the fix.
It would be great if these issues could be addressed in the upcoming VA 7.5 release, especially if the database framework is going to be changed for Oracle 10g and LOB support anyway.
Here is a list of the problems. Detailed descriptions follow below.
1.) Sporadic error message "AbtError does not understand >"
2.) OciBreak thread is not terminated on disconnect
3.) Cannot invoke stored procedures without parameters
4.) AbtDatabaseConnection>>#unRegister should not set databaseMgr to nil
5.) Resource leak if connect fails
6.) NUMBER(x,0) is handled as a scaled decimal, not as an integer
7.) nullsOk is preset incorrectly for host variables
8.) implementsStoredProcs should be a class method
9.) Oracle RAW fields broken with 6.0.3 change
10.) Stored procedure parameter lists hold references to freed OS objects after disconnect
11.) Raising exceptions in an error block causes cursor leak
Thanks and best regards,
Christoph
1.) Sporadic error message "AbtError does not understand >"
Reason: AbtOracleDatabaseConnection>>#errorTextFor: does not check for AbtError
It can occur that #callPlatformFunction:withArray:useThreadPreference:threadKey: returns an AbtError.
This case is not handled properly.
Fix: Change AbtOracleDatabaseConnection>>#errorTextFor: as follows.
- Code: Select all
errorTextFor: aReturnCode
"Answer the error text for the specified return code."
| textLength buf bufLength |
self isTraceActive ifTrue: [ self logTraceMessage: AbtDbmTraceGetMsgText. ].
buf := ByteArray new: Oci_Max_Error_Msg_Length.
bufLength := buf size.
textLength :=
self
callPlatformFunction: OracleOciOerhms
withArray:
(Array with: (self lda abtAsOraclePassedPointer) with: aReturnCode with: buf with: bufLength)
useThreadPreference: true
threadKey: nil.
textLength isAbtError
ifTrue: [
^'Internal error retrieving error text for %1: %2'
bindWith: aReturnCode printString
with: textLength errorText
].
^textLength > 0
ifTrue: [ (buf memcpyStringFrom: 0 to: textLength - 1) abrAsString. ]
ifFalse: [ 'Bogus error code'. ]
2.) OciBreak thread is not terminated on disconnect
Calling AbtOracleDatabaseConnection>>#breakIfError: causes an additional thread named '<ConnectionAlias>_OciBreak' to be created in #breakCallForLda:ifError:. This thread is not terminated on disconnect.
Fix: Add the following method in AbtOracleDatabaseConnection.
- Code: Select all
terminateThread
"Terminate thread for 'break' calls, too."
super terminateThread.
self alias notNil ifTrue: [ AbtThreadManager terminateThread: self alias , '_OciBreak'. ].
3.) Cannot invoke stored procedures without parameters
AbtOracleAbstractDatabaseConnection>>#invokeStoredProcedureNamed:withParameters:ifError: cannot handle stored procedures without parameters.
Fix: Change the method as follows.
- Code: Select all
invokeStoredProcedureNamed: aProcName withParameters: aParmList ifError: anErrorBlock
" "
| statementStream |
" Move this code into the spec itself "
(statementStream := WriteStream on: (Locale current preferredStringClass new: 128))
nextPutAll: 'BEGIN ';
nextPutAll: aProcName.
(aParmList isNil or: [ aParmList parameters isEmpty. ])
ifTrue: [
statementStream nextPutAll: '; END;'.
^self executeSQLStatement: statementStream contents ifError: anErrorBlock
]
ifFalse: [
statementStream nextPutAll: '( '.
aParmList doWithIndex: [ :aParm :anIndex |
aParm type fieldsDo: [ :aParmField |
anIndex > 1 ifTrue: [ statementStream nextPut: $,. ].
statementStream
nextPut: $:;
nextPutAll: aParmField name.
].
].
statementStream nextPutAll: '); END;'.
^self
executeStoredProcQuerySpec: statementStream contents abrAsQuerySpec
withParameters: aParmList
ifError: anErrorBlock
].
4.) AbtDatabaseConnection>>#unRegister should not set databaseMgr to nil
AbtDatabaseConnection>>#unRegister was changed in 6.0.2 (due to PQ77138, according to the comment):
- "critical" was added
- databaseMgr was set to nil
The latter caused a problem in our code, but I am not sure anymore where exactly. Possibly in #isTraceActive.
Fix: Leave the "critical" in there, but do not set databaseMgr to nil
- Code: Select all
unRegister
"Delete the receiver from the collection of active connections"
[self databaseMgr removeActiveConnection: self] critical
5.) Resource leak if connect fails
AbtOracleDatabaseManager>>#connectToDataSourceNamed:logonSpec:ifError: leaks resources leak if the connect fails.
After a lot of retries I get the error message "couldn't allocate a static future".
Fix: Invoke "workQueue terminate" and "terminateThread"
- Code: Select all
connectToDataSourceNamed: aDataSourceName logonSpec: aLogonSpec ifError: anErrorBlock
" Establish a new database connection. Answer the new AbtDatabaseConnection or
an AbtError. "
| rc newConnection |
newConnection := self newDatabaseConnection dataSourceName: aDataSourceName.
rc :=
newConnection
logonToDataSource: aLogonSpec server
id: aLogonSpec id
pw: aLogonSpec password
ifError: anErrorBlock.
^rc isAbtError
ifFalse: [ newConnection. ]
ifTrue: [
newConnection
freeHda;
freeLda.
newConnection workQueue terminate.
newConnection terminateThread.
rc.
]
6.) NUMBER(x,0) is handled as a scaled decimal, not as an integer
(a) Rows returned by a single or multiple row query contain ScaledDecimal values for fields of type NUMBER(x,y), even if y=0. However, logically, fields of type NUMBER(x,0) should be treated as (Large)Integers.
(b) The same holds for the type of host variables that is preset after creation of a query.
Partial fix for (b): Change AbtOracleAbstractDatabaseManager>>#fieldForColData: as follows.
- Code: Select all
fieldForColData: aColData
"Fix: recognize integer host variables correctly."
| fieldType precision scale |
fieldType := self class typeStringDict at: aColData columnType.
precision := aColData precision ifNil: [ 0. ].
scale := aColData scale ifNil: [ 0. ].
(fieldType = Oci_Number_Datatype and: [ scale = 0 and: [ precision ~= 0. ]. ])
ifTrue: [ fieldType := Oci_Integer_Datatype. ].
^self class
fieldForType: fieldType
length: aColData length
precision: precision
scale: scale
nullOk: aColData acceptsNulls
7.) nullsOk is preset incorrectly for host variables
#fieldForType:length:precision:scale:nullOk: has three implementors:
- AbtOdbcDatabaseManager
- AbtOracleDatabaseManager
- AbtOracle8DatabaseManager
The first expects the nullOk parameter to be a boolean, the latter two expect it to be an integer.
There are six senders of this method (Oracle + ODBC). Some of the Oracle ones pass a boolean, others an integer.
Fix: Standardize to boolean.
8.) implementsStoredProcs should be a class method
#implementsStoredProcs is sent by AbtStoredProcSettingsView to the database manager *class*.
However, in AbtDatabaseManager, it is implemented incorrectly as an instance method.
Fix: Make #implementsStoredProcs a class method in AbtDatabaseManager
9.) Oracle RAW fields broken with 6.0.3 change
In 6.0.3, AbtDatabaseBinaryField>>#put:intoRecord:parentOffset: was changed (obviously for some ODBC-related fix).
Unfortunately, this change broke the subclass AbtOracleRawField, because the invoked method #valueLengthFor: exists only for ODBC.
Fix/Workaround: Undo the change.
10.) Stored procedure parameter lists hold references to freed OS objects after disconnect
- An AbtStoredProc stores the list of parameter values for the stored procedure call in the instance variable "inputs".
- The parameter values are instances of AbtOracleRow and contain several OS pointers (columnLengths, returnCodes, ...).
- When the stored procedure is invoked, a PL/SQL block of the form "begin MyProc(myArguments); end;" is executed. The block also holds references to the parameter values, but not to the list.
- After the second invocation (?), the block is cached together with its arguments.
- On disconnect, the cache is flushed (flushCacheIfError:). This causes the OSPtr instances for the parameter values to be freed.
- The parameter list object in the stored procedure does not notice that.
- On the next stored procedure call (after a database reconnect), the broken parameter list with the freed pointers is passed and an exception is raised: "UndefinedObject does not understand abtAsSegmentedAddress"
Workaround: After database reconnect, set the instance variable "inputs" to nil.
11.) Raising exceptions in an error block causes cursor leak
As documented in Object class>>#abtDefaultDatabaseErrorBlock, returns from a database error block are not allowed.
I consider this a design bug. It should be possible to return/raise exceptions in an error block.
Fix: Use #ensure: in the framework methods to free resources (easier said than done, this would have to be changed in a lot of places ... )