Skip to main content



This section contains information of interest to those who have designed, developed and maintained applications running on prior versions of Caché.

The items listed here are brief descriptions. In most cases, more complete descriptions are available elsewhere in the documentation.

Routine Compiler Changes

Class Compiler Handles Long Class Names by Truncation

Although Studio prevents you from defining class names that exceed the allowed lengths, it is possible to generate code with names that are too long. In previous releases, this could result in a <NOROUTINE> error while compiling the class. In this release the class name is truncated to the maximum length.

Incremental Compile of Classes Feature Removed

In this release the /incremental compiler switch has been removed. In past releases, this switch instructed Caché to compile modified class methods only. Since the incremental compile provided only a minor reduction in compile time and actually produced less efficient code, this feature has been removed. If you specify the /incremental switch in this release, the compiler ignores the switch.

Compiler Disallows Illegal Class Names

In this release the class compiler produces an error if a class name contains either “||” (two vertical bars) or “.” (period). These symbols were not allowed in class names and typically, cause errors during execution, but were previously allowed by the class compiler.

Class Changes

Class Deletions

The following classes were present in the previous version and have been removed in this release

  • %CSP.UI.Portal.iKnow — Configurations, Dictionary, Settings

  • %CSP.UI.Portal.iKnow.Dialog — AddDomainConfig, DeleteDomain, finderDialog

  • %DeepSee.DomainExpert — SQLUtils

  • %DeepSee.DomainExpert.queries — IK

  • %DeepSee.DomainExpert.utils — ConfigurationManager, CubeUtils, EntityUtils, HtmlUtils, IndexBuilder, LinkedLifeData, SentenceParser, SetUtils, UMLSUtils, Utils

  • %DeepSee.DomainExpert.utils.classgen — CubeGen

  • %DeepSee.DomainExpert.utils.umls — EntityMappingBuilder, EntityTableGenerator, LocalMappingBuilder

  • %DeepSee.DomainExpert.wizard.Dialog — newModel

  • %DeepSee.DomainExpert.wizards — HeaderKeyExplorer, ITablesLoadingWizard, TransformationEditor

  • %JSON — ContentHandler, Parser

  • %SYS — TaskSuper

  • %SYSTEM.Security — System


  • %UMLS.Install — CBuilder, Scripts

  • %UnitTest — DSQL, ESQL, ITablesRegression, JDBCSQL, ODBCSQL, Utility

  • %iFind — Comp, CompToWord, Stem, StemToWord

  • %iFind.Find — Abstract, Basic, Semantic

  • %iFind.Index — AbstractAttribute, AbstractAttributePos, AbstractDominance, AbstractEntBits, AbstractEntSpread, AbstractEntSpreadRec, AbstractEntity, AbstractPath, AbstractPathVal, AbstractProximity, AbstractWordPos

  • %iKnow.Objects.dd.ui — HistoryDialog, SearchHistory

  • %iKnow.UI — ITablesStatusView, ImportWizard, LoadingWizard, SetAnalysis, SwirlPortal

  • %iKnow.ont — Matcher, Matcher2, SQLStoredProc

  • SYS.Monitor — SystemSubscriber

Removed Methods

The following methods were present in the previous version and have been removed in this release

  • %CSP.Page

    • HyperEventBody

    • HyperEventFrame

  • %CSP.Request

    • Delete

    • DeleteCgiEnv

    • DeleteCookie

    • DeleteMimeData

    • InsertCgiEnv

    • InsertCookie

    • InsertMimeData

    • Set

  • %CSP.Stream

    • CharSetGet

    • CharSetSet

    • ContentTypeGet

    • ContentTypeSet

    • ExpiresGet

    • ExpiresSet

    • FileNameGet

    • FileNameSet

    • HeadersGet

    • HeadersSet

    • MimeSectionGet

    • MimeSectionSet

  • %CSP.UI.Portal.TaskHistory

    • doDetails

  • %CSP.UI.Portal.TaskSchedule

    • ExecuteRS

  • %DeepSee.UI.MDXPDF

    • Test

  • %iFind.Find.Basic

    • FindResultsLevel

    • FindResultsNodeFindResultsNode

    • FindResultsPattern

    • GetExtent

    • GetWordBitsBeg

    • GetWordBitsFin

    • GetWordBitsMid

    • ParseInput

    • ResolveCompCombArray

    • ResolveCompWords

    • ResolveCompoundToWords

    • ResolveCompoundToWordsAND

    • ResolveStemToWords

    • ResolveStemmedCombArray

    • ResolveStemmedWords

    • SearchBeg

    • SearchFin

    • SearchMid

    • SearchPos

    • SearchPosD

    • SearchPosInternal

    • SearchPosS

    • SearchWrd

    • SearchWrdD

    • SearchWrdS

    • WordBitsToRecIds

    • WordToRecIds

  • %iFind.Find.Semantic

    • BuildRecordIds

    • BuildRecordIdsAnd

    • CheckEntBeg

    • CheckEntFin

    • CheckEntGrp

    • CheckEntGrpD

    • CheckEntGrpEnt

    • CheckEntGrpEntD

    • CheckEntGrpEntS

    • CheckEntGrpS

    • CheckEntMid

    • CheckEntWrd

    • CheckEntWrdD

    • CheckEntWrdS

    • CheckGrp

    • CheckGrpD

    • CheckGrpEnt

    • CheckGrpEntD

    • CheckGrpEntS

    • CheckGrpS

    • FindResultsLevel

    • GetEntsByWordPos

    • ParseInput

  • %iFind.Index.Basic

    • GetIndexParams

  • %iKnow.UI.IndexingResults

    • UpdateSentences

    • updateSentencesClient

Removed Properties

The following properties were present in the previous version and have been removed in this release

  • %CSP.UI.Portal.Locks

    • isVMS

  • %CSP.UI.Portal.LocksView

    • isVMS

  • %Exception.CPPException

    • iStack

  • %iKnow.UI.IndexingResults

    • sentFrom

    • sentTo

Removed Parameters

The following parameters were present in the previous version and have been removed in this release

  • %Library.CacheLiteral


  • %SQL.Manager.ShowPlan


  • %UnitTest.SQLRegression





Also, the property parameter SELECTIVITY is now deprecated and is no longer shown in Studio or in the documentation. If the parameter is present, it has the same effect as in previous releases. Customers are encouraged to remove this property parameter from existing code.

Removed Indexes

The following indexes were present in the previous version and have been removed in this release

  • %iFind.Comp

    • ValueIdx

  • %iFind.Stem

    • ValueIdx

Modified Methods

The following methods have different signatures in this version of Caché:

  • %CSP.UI.Portal.Mirror.Monitor

    • old method: DrawFailoverInfo () As %Status

    • new method: DrawFailoverInfo (RefreshCompress) As %Status

    • old method: updateView () As (none)

    • new method: updateView (InTimer, NoCompress) As (none)

  • %CSP.UI.Portal.Namespaces

    • old method: filterChanged (value) As (none)

    • new method: filterChanged (value, tableID) As (none)

  • %CSP.UI.Portal.TaskSchedule

    • old method: filterChanged (value) As (none)

    • new method: filterChanged (value, tableID) As (none)

  • %Debugger.System

    • old method: DebugStub () As (none)

    • new method: DebugStub (pWriteOutput) As (none)

  • %DeepSee.Component.Widget.pivot

    • old method: exportPDF () As (none)

    • new method: exportPDF (printMultiple, preserveTempFiles) As (none)

  • %DeepSee.ResultSet

    • old method: %GetFiltersForCellRange (*pFilters:%String, pStartRow:%Integer, pStartCol:%Integer, pEndRow:%Integer, pEndCol:%Integer, *pMeasure:%String, pAllRows:%Boolean=0, pAllCols:%Boolean=0, pSortCol:%Integer=0, pSortDir:%String="ASC") As %Status

    • new method: %GetFiltersForCellRange (*pFilters:%String, pStartRow:%Integer, pStartCol:%Integer, pEndRow:%Integer, pEndCol:%Integer, *pMeasure:%String, pAllRows:%Boolean=0, pAllCols:%Boolean=0, pSortCol:%Integer=0, pSortDir:%String="ASC", &pPluginInfo:%String) As %Status

  • %DeepSee.UI.Analyzer

    • old method: adjustSizes () As (none)

    • new method: adjustSizes (load) As (none)

  • %DeepSee.UserPortal.DashboardViewer

    • old method: ClearDashboardAutosaveState (pAutosaveItems:%ZEN.proxyObject) As %Status

    • new method: ClearDashboardAutosaveState (pAutosaveItems:%ZEN.proxyObject, pDashboardName:%String, pAutosave:%String) As %Status

  • %DeepSee.Utils

    • old method: %CompressIndices (pCubeName:%String, pVerbose:%Boolean=0) As (none)

    • new method: %CompressIndices (pCubeName:%String, pVerbose:%Boolean=0) As %Status

  • %Library.Persistent

    • old method: %BuildIndices (pIndexList:%CacheString="", pAutoPurge:%Integer=0, pLockFlag:%Integer=0, pJournalFlag:%Integer=0, pStartID:%CacheString="", pEndID:%CacheString="") As %Status

    • new method: %BuildIndices (pIndexList:%CacheString="", pAutoPurge:%Integer=1, pLockFlag:%Integer=0, pJournalFlag:%Integer=1, pStartID:%CacheString="", pEndID:%CacheString="") As %Status

    • old method: %DeleteExtent (concurrency:%Integer=-1, &deletecount, &instancecount, pInitializeExtent:%Integer=1) As %Status

    • new method: %DeleteExtent (concurrency:%Integer=-1, &deletecount, &instancecount, pInitializeExtent:%Integer=1, *errorLog:%Status) As %Status

  • %Library.RoutineMgr

    • old method: TS (name:%String, &compiletime:%TimeStamp) As %TimeStamp

    • new method: TS (name:%String, *compiletime:%TimeStamp, *uptodate:%Boolean) As %TimeStamp

    • old method: GetCurrentTimeStamp (&compiletime:%TimeStamp) As %TimeStamp

    • new method: GetCurrentTimeStamp (&compiletime:%TimeStamp, &uptodate:%Boolean) As %TimeStamp

  • %SYSTEM.DeepSee

    • old method: SynchronizeCube (pCubeName:%String, pVerbose:%Boolean=0) As %Status

    • new method: SynchronizeCube (pCubeName:%String, pVerbose:%Boolean=0, *pFactsUpdated:%Integer, pReadCommitted:%Boolean=1, pCheckReferences:%Boolean=1, pAsync:%Boolean=0) As %Status


    • old method: CompileList (&list:%String="", qspec:%String="", &errorlog:%String) As %Status

    • new method: CompileList (&list:%String="", qspec:%String="", &errorlog:%String, &updatedlist:%String) As %Status

  • %SYSTEM.iKnow

    • old method: GetDomainId (pDomainName:%String) As %Integer

    • new method: GetDomainId (pDomainName:%String="") As %Integer

  • %Stream.Object

    • old method: FindAt (position:%Integer, target:%CacheString, &tmpstr:%CacheString, caseinsensitive:%Boolean=0) As %Integer

    • new method: FindAt (position:%Integer, target:%CacheString, &tmpstr:%CacheString="", caseinsensitive:%Boolean=0) As %Integer

  • %Studio.Project

    • old method: realCompile (qstruct, &%errorlog:%String, &itemlist:%String) As %Status

    • new method: realCompile (qstruct, &%errorlog:%String, &itemlist:%String, &updatedlist:%String) As %Status

  • %XML.Node

    • old method: FirstAttributeName () As (none)

    • new method: FirstAttributeName () As %String

    • old method: LastAttributeName () As (none)

    • new method: LastAttributeName () As %String

    • old method: NextAttributeName (attributeName:%String) As (none)

    • new method: NextAttributeName (attributeName:%String) As %String

    • old method: PreviousAttributeName (attributeName:%String) As (none)

    • new method: PreviousAttributeName (attributeName:%String) As %String

  • %XML.Writer

    • old method: Document (document:%XML.Document) As %Status

    • new method: Document (documentArg:%XML.Document) As %Status

    • old method: Object (object:%XML.Adaptor, tag:%String, namespace:%String, local:%Boolean, className:%String, bare:%Boolean) As %Status

    • new method: Object (objectArg:%XML.Adaptor, tag:%String, namespace:%String, local:%Boolean, className:%String, bare:%Boolean) As %Status

  • %ZEN.Mojo.Wizard.MojoWizard

    • old method: CreateZenMojoTemplate (tempName:%String, pkgName:%String, application:%String, domain:%String, pPageManager:%String) As %Status

    • new method: CreateZenMojoTemplate (tempName:%String, pkgName:%String, application:%String, domain:%String, pageName:%String, pPageManager:%String) As %Status


    • old method: %DrawToHTML (&context:%String, &XSL:%GlobalCharacterStream, delay:%GlobalCharacterStream, &incell:%Boolean, embedXSL:%Boolean=0, UseInternalXSLT:%Boolean=0, SubReport:%String, MainReport:%String) As %Status

    • new method: %DrawToHTML (&context:%String, &XSL:%GlobalCharacterStream, delay:%GlobalCharacterStream, &incell:%Boolean, embedXSL:%Boolean=0, UseInternalXSLT:%Boolean=0, SubReport:%String, MainReport:%String, pXSLTMode) As %Status

  • %iFind.Utils

    • old method: TestSearchString (pSearchString:%String, *pNormalizedString:%String) As %Status

    • new method: TestSearchString (pSearchString:%String, *pNormalizedString:%String, *pDidYouMean:%String) As %Status

  • %iKnow.Tables.Utils

    • old method: SyncCustomizations (pDomainId:%Integer="", pPackageName:%String="", pAutoPurge:%Boolean=0, pStartSrcId:%Integer="", pEndSrcId:%Integer="", &pRanges) As %Status

    • new method: SyncCustomizations (pDomainId:%Integer="", pPackageName:%String="", pAutoPurge:%Boolean=1, pStartSrcId:%Integer="", pEndSrcId:%Integer="", &pRanges) As %Status

    • old method: SyncDictionaryMatchCustomizations (pDomainId:%Integer="", pPackageName:%String="", pAutoPurge:%Boolean=0, pStartSrcId:%Integer="", pEndSrcId:%Integer="", pFilter:%iKnow.Filters.Filter="") As %Status

    • new method: SyncDictionaryMatchCustomizations (pDomainId:%Integer="", pPackageName:%String="", pAutoPurge:%Boolean=1, pStartSrcId:%Integer="", pEndSrcId:%Integer="", pFilter:%iKnow.Filters.Filter="") As %Status

  • Ens.MessageHeader

    • old method: NewRequestMessage (&pHeader:Ens.MessageHeader, pMessageBody:%Library.Persistent, &pSessionId:%String) As %Status

    • new method: NewRequestMessage (&pHeader:Ens.MessageHeader, pMessageBody:%Library.Persistent, &pSessionId:%String, &pSuperSession:%String) As %Status

  • Ens.VDoc.SearchTable

    • old method: genGetCodeList (tGetExpression:%String) As %String

    • new method: genGetCodeList (tGetExpression:%String, tPreExpression:%String, tPostExpression:%String) As %String

  • Ens.VDoc.XMLSearchTable

    • old method: genGetCodeList (tGetExpression:%String) As %String

    • new method: genGetCodeList (tGetExpression:%String, tPreExpr:%String, tPostExpr:%String) As %String

Class Compiler Changes

This version of Caché continues the work begun in earlier releases of improving the class compiler. The changes that may require changes to applications are detailed in this section.

Language Binding Changes

There are no compatibility issues in this area.

SQL Changes

UNION %PARALLEL is not allowed in INSERT, UPDATE, and DELETE Queries

In past releases, %PARALLEL was not allowed in non-union INSERT, UPDATE, and DELETE Queries, but did not cause an error when you used %PARALLEL in a union INSERT, UPDATE, or DELETE query. The %PARALLEL did not work reliably in these queries. In this release, %PARALLEL is not allowed in either a union or non-union INSERT, UPDATE, and DELETE query. If you had used this construct in a previous release, it would not have caused an error message, but would likely have caused problems. You should remove any %PARALLEL option that is present in an INSERT, UPDATE, or DELETE query.

Fix to Floor Function Changes Returned Value

In previous versions, the ODBC Floor() function did not properly truncate values when the return type was a $list type 6 with an integer value and an SQLType Numeric (2) with zero scale. For example, the function could return “1234.” with an incorrect trailing decimal point. In this release, the Floor() function correctly truncates the number returning “1234” without the decimal point. If you have coded your application expecting this incorrect result and compensating for it, you should remove this correction code. This error only occurred with SQLType Numeric (2) and a zero scale. It did not occur with other types or nonzero scales.

Collation Expressions Are Checked for Errors

In this release Caché checks collation expressions for errors. In previous releases, Caché ignored these errors. In most cases, the error in the collation expression caused an error when using SQL, but it is possible that some collation expression errors did not cause an SQL error. In these cases, you will encounter an error in this release in code that appeared to execute successfully in previous releases.

Aggregate Functions Now Handle Zero-Length String Correctly

Aggregate Functions Now Handle Zero-Length String Correctly In this release, the XMLAGG() and LIST() aggregate functions handle 0-length strings correctly. If code relies on the incorrect way previous releases handled 0-length strings, it should be modified.

JDBC Data Models Have Significant Internal Changes to Support Read Ahead

This release contains many performance improvements for SQL JDBC data models. We do not know of any incompatibilities caused by these changes, but it is possible that implementation detail changes may influence behavior in unusual circumstances. We recommend that you test code that exercises this feature.

Changes To Locale And I/O Translation Tables

Functions Converting XML and ODBC Times Locale Change

The %Library.TimeOpens in a new tab methods that are converting from XML and ODBC time formats used the current locale in previous releases. This caused a problem in locales where the decimal separator is not a . (period character) because XML and ODBC always uses a period character for this separator. In this release, these functions always use a period for the decimal separator. If you are calling these methods to set or evaluate XML or ODBC messages, there is no problem. But, if your code uses these methods in another context and expects the current locale’s decimal separator, you need to modify your code. The changed %Library.TimeOpens in a new tab methods are: LogicalToXSD(), XSDToLogical(), LogicalToOdbc(), and OdbcToLogical().

The %Library.TimeOpens in a new tab methods were inconsistent about truncating fractional seconds in previous releases. In this release, by default the methods always retain fractional seconds. There is a new PRECISION parameter that controls the number of decimal places to keep. If PRECISION equals 0, the time value will be rounded to the nearest second.

iKnow Changes

Compiling an iFind Index Requires Software License

The use of iFind has always required a valid iKnow license. Starting with this release, Caché will verify whether there is an iKnow-enabled license for your instance when you compile a class with an iFind index and an error will be thrown if you are not properly licensed for using iKnow. There is no strict need to recompile classes with iFind indices after upgrading to 2015.3, though it is generally recommended because it can provide improved optimizations.

Certain iFind Indexes Require Recompiling and Rebuilding

iFind indexes that use stemming or decompounding (INDEXOPTION != 0) have a changed compiled form. Consequently, after upgrading to this release, you must recompile and rebuild any existing indexes that use stemming or decompounding.

We recommend that you use the new %iFind.OriginalWord and %iFind.WordTransformation tables instead of relying on the specific mappings for stems and compounds. While using the specific mappings remain a supported feature, using the new tables is the recommended practice.

CSP Changes

There are no compatibility issues in this area.

SMTP, XML, Web Services, SOAP, And REST Changes

Converting from JSON to Object Error Handling Changed

In previous releases, the $fromJSON() function would fail silently but not return a valid object. In this release, if the function encounters an error condition, it throws an error. You should ensure that the error does not interrupt your application by including the $fromJSON() call in a try-catch block.

For example, if you had the following code to check that the $fromJSON() function returned a valid object:

  set obj = ##class(%AbstractObject).$fromJSON(source [,.stats]) 
  if ('$isobject(obj)) { 
    w "Compilation error occurred",! q 

You should replace it with a try-catch block such as:

  try { 
    set obj = ##class(%AbstractObject).$fromJSON("[1,2,WE WILL FAIL HERE") 
  } catch ex { 
    w "JSON Parsing error",! 
    w "Name = "_ex.Name,! 
    w "Code = "_ex.Code,! 
    w "Location = "_ex.Location,! 

Behavior in Entering SMTP Password Changed

In previous releases, Caché queried for the SMTP password only a single time. In this release Caché queries twice for the passsword and checks to see that the same password was entered to each prompt. This change eliminates the condition where Caché was attempting to use a null string as a password.

If you have written a script that responds to the password prompt, you must modify the script to handle the second password request.

DeepSee Changes

MDX Context is Provided without Encryption

In order to fix problems with some locales, this release does not persist the MDX context and does not encrypt it. If you are using custom action code in %OnDashboardAction that references the MDX context (pContext.mdx), you will have to modify your code and recompile. For example, if you have the following code to decrypt the MDX context:

Set tMDX = $$$cspDecode(%session.Key,pContext.mdx) 

You should replace it with the following that directly references the MDX context without decrypting it:

Set tMDX = pContext.mdx

If you do not make this change the cspDecode call causes an error.

New Option for Members with Numeric Names

If your cubes contain any levels that are based on source expressions and that have members with numeric names, it is possible to receive incorrect results in a very specific scenario.

The problem scenario is when a query uses an MDX range expression — for example, ([UnitsPerTransaction].[H1].[UnitsSold].&[7]:&[10])and one of the end point members does not exist. In such a scenario, DeepSee attempts to choose a different end point member. If the level is based on a source expression, DeepSee does not have the necessary information to choose a replacement member correctly and thus would sometimes choose the wrong member. In this scenario, it is necessary to provide additional information to indicate that DeepSee should treat the members as numeric.

InterSystems recommends that you review the level definitions in all your cubes and then do the following:

  1. If the level is based on a source property, no change is needed.

  2. If the level has members with purely numeric names, modify the cube in Studio and add castAsNumeric="true" to that level definition.

    This option is different from the Sort option, which does not affect how DeepSee searches for a new end point member.

  3. Recompile the cube class.

    It is not necessary to rebuild the cube.

  4. If you have previously encountered the problem scenario and if you might have results cached for the problem queries, reset the result cache.

    To reset the result cache, use the %Reset() method of %DeepSee.UtilsOpens in a new tab. Note that this method also has an immediate impact on any users and is generally intended only for use during development. Also note that it affects only the current namespace.

pContext.mdx No Longer Encrypted

In previous releases, within the %OnDashboardAction() method, the underlying MDX query was provided in encrypted form. Specifically, when the data source is a pivot table, pContext.mdx contains the MDX query. In previous releases, it was necessary to use code like the following to decrypt the query:

 set myvariable = $$$cspDecode(%session.Key,pContext.mdx)

Now you can use code like the following instead:

 set myvariable = pContext.mdx
Scorecards Based on Pivot Tables with Crossjoins May Need to be Modified

This releases fixes a problem with scorecard widgets that are based on pivot tables with crossjoins. If you have a scorecard widget that meets all the following criteria, then you will need to edit the widget and reselect the properties for the scorecard. The criteria are:

  • It is a scorecard widget.

  • The data source for the scorecard is a pivot table.

  • The pivot table has one level in Columns and one measure in Rows.

You only need to edit the scorecard if all of the criteria are true. You do not need to edit a scorecard that has multiple measures or one that is based on a KPI.

Labels for Pivot Tables with a Single Measure

In prior releases, pivot tables were inconsistent in how they displayed measure headers if there was only one measure. A singe calculated measure always displayed a measure header, but a single non-calculated measure did not display a measure header. Pivot tables are now consistent in how they display measure headers. The default is to display a header for the measure if there is more than one measure (calculated or not calculated). The can be changed by using Measure Options.

If you have a pivot table with a single calculated measure and still want to see the measure header, use Measure Options and change Display Measure Headers to Always.

Zen Changes

Improved Handling of Long Labels and Titles May Change Output

This release improves how Zen handles long labels and titles and eliminates overlap between them. If the labels would take up more space than the chart itself, Zen displays the chart with the y axis titles only and suppresses the labels. In some cases, this improved handling of labels and titles changes the way charts are displayed.

Reports Now Require a Body Element

In this release the Zen Reports compiler requires that a report have a body element. In most cases, the error is useful and points to a report that needs to be fixed. In rare cases a valid report might not need a body element. These reports will not successfully compile. You should add a body element to these reports.

Zen Mojo Changes

There are no compatibility issues in this area.

System and Utilities Changes

Custom Callout Library Functions that Set TZ Need to be Modified

In order to make the time functions reentrant and secure, in certain environments you need to modify the code that sets the TZ environment variable. If you are running on Linux or Windows, you will need to add a call to tzset() after setting the TZ environment variable in a custom callout library function. The call to tzset() forces the update of the internal variables tzname, timezone, and daylight.

Changes to Output from Integrity Check May Cause Problems if Code is Dependent on Log Format

Improvements made to the integrity check, caused changes in the log output. If you have code that is dependent on the exact text in the log message, you need to revise it.

Lock Command Deferred Unlock ("D" mode) Changes

In previous releases, the behavior of "D" mode unlock was not well defined in the case of nested locks for the same lock node. This has been improved so that, "D" mode unlock respects the state of prior unlocks even when the prior unlocks were nested within an "outer" lock that is unlocked in "D" mode.

Unit Test Changes

New Assert Failure Macro

In this release, the new $$$AssertFailure macro unconditionally fails a test and logs the specified message. If the /debug qualifier was specified, the test will break in the debugger at that point. Following the convention of the other assertions, it returns 0, indicating failure. This macro is intended to replace the convention of asserting false, such as in a try block after an exception is expected in a test.

This is only a compatibility issue if you have subclassed the UnitTest class and the new macro conflicts with an extension to the base class.

FeedbackOpens in a new tab