IBoldDatabase.CreateAnotherDatabaseConnection hands out a new connection wrapper, but the interface has no matching way to dispose of it. The wrappers are TBoldNonRefCountedObjects, so the two adapter-neutral callers leak one wrapper per extra connection:
TBoldDbCopy.ProcessTables (Source/Persistence/DB/BoldDbCopy.pas) creates a source and a destination wrapper per worker, releases their queries and Closes them in the finally - and lets the locals go out of scope.
TBoldDbValidatorThread.Execute (Source/PMapper/Validator/BoldDbValidator.pas) creates one per validator thread and ends with fBoldDatabase := nil.
Found while fixing #90 and #91 (the UniDAC wrapper's own leaks). With those fixed, freeing the wrapper would now clean up its cached queries and its owned DAC connection - but nothing frees it.
Root cause
The callers cannot free what they hold: IBoldDatabase.Implementor is the DAC component (TFDConnection / TUniConnection), not the wrapper, and casting to TBoldFireDACConnection or TBoldUniDACConnection - what the FireDAC unit test does - is not available to adapter-neutral code. Every other resource of the interface comes as a pair (GetQuery/ReleaseQuery, GetExecQuery/ReleaseExecQuery, GetTable/ReleaseTable); CreateAnotherDatabaseConnection has no Release... counterpart.
Fix
IBoldDatabase.ReleaseAnotherDatabaseConnection(var ADatabase: IBoldDatabase), implemented once in TBoldDatabaseWrapper (every adapter's connection class descends from it): obtain the wrapper object from the interface (as TObject), nil the reference, free the object. The reference is nilled before the free because the wrapper is not reference counted but assigning nil still calls _Release.
TBoldDbCopy.ProcessTables: release both wrappers in the finally.
TBoldDbValidatorThread.Execute: release the wrapper instead of dropping the reference.
- The FireDAC and UniDAC tests that dispose an extra connection use the new method instead of a class cast.
Files changed
Source/Persistence/DB/BoldDBInterfaces.pas
Source/Persistence/DB/BoldDbCopy.pas
Source/PMapper/Validator/BoldDbValidator.pas
UnitTest/Code/Persistence/Test.PersistenceFireDAC.pas, UnitTest/Code/Persistence/UniDAC/Test.PersistenceUniDAC.pas
Testing
IBoldDatabase.CreateAnotherDatabaseConnectionhands out a new connection wrapper, but the interface has no matching way to dispose of it. The wrappers areTBoldNonRefCountedObjects, so the two adapter-neutral callers leak one wrapper per extra connection:TBoldDbCopy.ProcessTables(Source/Persistence/DB/BoldDbCopy.pas) creates a source and a destination wrapper per worker, releases their queries andCloses them in thefinally- and lets the locals go out of scope.TBoldDbValidatorThread.Execute(Source/PMapper/Validator/BoldDbValidator.pas) creates one per validator thread and ends withfBoldDatabase := nil.Found while fixing #90 and #91 (the UniDAC wrapper's own leaks). With those fixed, freeing the wrapper would now clean up its cached queries and its owned DAC connection - but nothing frees it.
Root cause
The callers cannot free what they hold:
IBoldDatabase.Implementoris the DAC component (TFDConnection/TUniConnection), not the wrapper, and casting toTBoldFireDACConnectionorTBoldUniDACConnection- what the FireDAC unit test does - is not available to adapter-neutral code. Every other resource of the interface comes as a pair (GetQuery/ReleaseQuery,GetExecQuery/ReleaseExecQuery,GetTable/ReleaseTable);CreateAnotherDatabaseConnectionhas noRelease...counterpart.Fix
IBoldDatabase.ReleaseAnotherDatabaseConnection(var ADatabase: IBoldDatabase), implemented once inTBoldDatabaseWrapper(every adapter's connection class descends from it): obtain the wrapper object from the interface (as TObject), nil the reference, free the object. The reference is nilled before the free because the wrapper is not reference counted but assigning nil still calls_Release.TBoldDbCopy.ProcessTables: release both wrappers in thefinally.TBoldDbValidatorThread.Execute: release the wrapper instead of dropping the reference.Files changed
Source/Persistence/DB/BoldDBInterfaces.pasSource/Persistence/DB/BoldDbCopy.pasSource/PMapper/Validator/BoldDbValidator.pasUnitTest/Code/Persistence/Test.PersistenceFireDAC.pas,UnitTest/Code/Persistence/UniDAC/Test.PersistenceUniDAC.pasTesting
TestReleaseAnotherDatabaseConnectionFreesWrapper(FireDAC, default suite): create + release an extra connection through the interface; after a warm-up pass the allocated-block count must be unchangedDebugUniDACTest.PersistenceUniDAC7/7 on SQL ServerLimitationClosed by the end-to-end tests:Test.BoldDbValidator.TTestBoldDbValidatorEndToEnd(TBoldDbValidator never frees its worker threads #93) andTest.BoldDbCopy.TTestBoldDbCopyEndToEnd(TBoldDbCopy fails silently on FireDAC: Prepare before parameter types, swallowed worker exceptions, leaked count query #94) now drive a real validator run and a real copy through both callers. Original note: neitherTBoldDbCopy.ProcessTablesnor a real validator thread is driven end to end by a unit test (the DbCopy tests are helper-level, the validator test spawns plain threads), so the two caller changes are verified by inspection and by the method's own tests