OCL collect over a role - self.child->collect(parent), or the implicit form self.child.parent, which BoldOclSemantics rewrites into the same collect node - cannot be evaluated in the persistence layer. CanEvaluateInPS answers False on every engine and Bold falls back to in-memory evaluation, which is correct but fetches the whole intermediate collection for large sets. Split out of #43, where the investigation showed this is a missing feature, not an engine bug.
Current state
TBSS_collect exists in Source/PMapper/SQL/BoldSqlSymbols.pas only as a commented-out placeholder (one of ~48 TBSS_* names inside a single { } block, lines ~455-520 - not declarations), so nothing installs it in the SQL symbol dictionary. TBoldSqlNodeResolver.VisitTBoldSqlIteration therefore raises InPs SQLSymbol 'collect' not found and TBoldSystemDefaultMapper.CanEvaluateInPS reports False.
collect(attribute) (self.child->collect(aString)) already works: the OCL semantics turn it into member navigation (TBoldSqlMember), so no iteration node is built.
- The regression test
Test.BoldLinks.TTestBoldLinks.TestCanEvaluateInPS_CollectRole_NoFalsePositive pins the current answer (False, all engines) and prints GetBoldLastFailureReason. When this feature lands, that test flips to expecting True and the fetch results must be compared with in-memory evaluation.
Where it slots in
The installed iterations (select, reject, exists, forAll, orderBy) are TBSS_Iteration descendants: ResolveObjectMapper returns Args[0].ObjectMapper (the source collection's class) and BuildWCFOrQuery shapes the loop variable's query (see TBSS_Exists.BuildWCFOrQuery: takes the loop var's query, adds the body's WCF, wraps it as EXISTS). collect(role) differs in one respect: the result is the other end's class, so
TBSS_collect must become a TBSS_Iteration descendant whose ResolveObjectMapper returns the object mapper of the body expression (Args[1], the role's other end), not of the source;
BuildWCFOrQuery must produce a query over the other end's table constrained by the link: for a single-valued role stored as an embedded foreign key, a join from the source rows' BOLD_ID to the other end's id column; for a multi-valued role, a join through the link table (or the reverse embedded key on the other end). TBoldLinkDefaultMapper / TBoldDirectMultiLinkDefaultMapper know the columns; TBSS_ListOperations.CreateBoldIdMatchWCF shows the id-matching WCF shape already used for includes;
- the node maker path for the implicit form (
BoldOclSemantics.pas sets OperationName := 'Collect') must reach the same symbol - check the name lookup is case-insensitive or aligned;
- duplicates: OCL
collect yields a bag; decide whether the SQL form may return distinct ids (Bold object lists are sets of locators anyway) and document it;
- install the symbol in the initialization list next to the other iterations.
Acceptance
Implemented in 27ab038
TBoldSqlSymbol gained LoopVarIsEnclosing and ResolveResultObjectMapper; TBSS_collect is a TBSS_Iteration descendant (loop variable continues the source query, result mapper = body's other end, body query taken over with DISTINCT = set semantics), installed after TBSS_Reject. A body that yields no objects is refused with sCollectBodyMustYieldObjectsInPS. Verified green: SQLite and SQL Server full suites (2181/2179/2 ignored/0 failed), DebugUniDAC fixtures 12/12 and Test.BoldLinks 85/85 over the UniDAC adapter.
OCL
collectover a role -self.child->collect(parent), or the implicit formself.child.parent, whichBoldOclSemanticsrewrites into the samecollectnode - cannot be evaluated in the persistence layer.CanEvaluateInPSanswersFalseon every engine and Bold falls back to in-memory evaluation, which is correct but fetches the whole intermediate collection for large sets. Split out of #43, where the investigation showed this is a missing feature, not an engine bug.Current state
TBSS_collectexists inSource/PMapper/SQL/BoldSqlSymbols.pasonly as a commented-out placeholder (one of ~48TBSS_*names inside a single{ }block, lines ~455-520 - not declarations), so nothing installs it in the SQL symbol dictionary.TBoldSqlNodeResolver.VisitTBoldSqlIterationtherefore raisesInPs SQLSymbol 'collect' not foundandTBoldSystemDefaultMapper.CanEvaluateInPSreportsFalse.collect(attribute)(self.child->collect(aString)) already works: the OCL semantics turn it into member navigation (TBoldSqlMember), so no iteration node is built.Test.BoldLinks.TTestBoldLinks.TestCanEvaluateInPS_CollectRole_NoFalsePositivepins the current answer (False, all engines) and printsGetBoldLastFailureReason. When this feature lands, that test flips to expectingTrueand the fetch results must be compared with in-memory evaluation.Where it slots in
The installed iterations (
select,reject,exists,forAll,orderBy) areTBSS_Iterationdescendants:ResolveObjectMapperreturnsArgs[0].ObjectMapper(the source collection's class) andBuildWCFOrQueryshapes the loop variable's query (seeTBSS_Exists.BuildWCFOrQuery: takes the loop var's query, adds the body's WCF, wraps it asEXISTS).collect(role)differs in one respect: the result is the other end's class, soTBSS_collectmust become aTBSS_Iterationdescendant whoseResolveObjectMapperreturns the object mapper of the body expression (Args[1], the role's other end), not of the source;BuildWCFOrQuerymust produce a query over the other end's table constrained by the link: for a single-valued role stored as an embedded foreign key, a join from the source rows'BOLD_IDto the other end's id column; for a multi-valued role, a join through the link table (or the reverse embedded key on the other end).TBoldLinkDefaultMapper/TBoldDirectMultiLinkDefaultMapperknow the columns;TBSS_ListOperations.CreateBoldIdMatchWCFshows the id-matching WCF shape already used forincludes;BoldOclSemantics.passetsOperationName := 'Collect') must reach the same symbol - check the name lookup is case-insensitive or aligned;collectyields a bag; decide whether the SQL form may return distinct ids (Bold object lists are sets of locators anyway) and document it;Acceptance
CanEvaluateInPS('self.child->collect(parent)')isTrueon SQLite and SQL Server;TestCanEvaluateInPS_CollectRole_NoFalsePositiveinverted accordingly (and renamed)self.child.parenttakes the same pathTestCanEvaluateInPS_MultiNavigationgets real assertions instead ofAssert.PassDebugUniDACImplemented in 27ab038
TBoldSqlSymbolgainedLoopVarIsEnclosingandResolveResultObjectMapper;TBSS_collectis aTBSS_Iterationdescendant (loop variable continues the source query, result mapper = body's other end, body query taken over withDISTINCT= set semantics), installed afterTBSS_Reject. A body that yields no objects is refused withsCollectBodyMustYieldObjectsInPS. Verified green: SQLite and SQL Server full suites (2181/2179/2 ignored/0 failed), DebugUniDAC fixtures 12/12 andTest.BoldLinks85/85 over the UniDAC adapter.