浏览代码

HPCC-10549 Use graph results for spilled wu results

This pull request contains a few related changes
- A new function for accesing a single row result.  It improves the
  generated code a bit (not as much as I hoped).
- Better serializitation of fields inside onStart - reduces code.
- Options to generate graph results for all spills, or just those spills
  that will be accessed from a child query
- Implement resolveLocalQuery(0) in hthor and roxie
- Add a way of conditionally including a compound statement (e.g,m a function
  definition) if other - non boilerplate code - was added.
- Add a way of conditionally generating code if another statement is included.
- Flag which graph results are used from child queries

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 11 年之前
父节点
当前提交
b87ee291f2

+ 1 - 0
ecl/eclagent/agentctx.hpp

@@ -28,6 +28,7 @@ struct IHThorGraphResult : extends IInterface
     virtual const void * queryRow(unsigned whichRow) = 0;
     virtual void getLinkedResult(unsigned & count, byte * * & ret) = 0;
     virtual const void * getOwnRow(unsigned whichRow) = 0;      // used internally, removes row from result
+    virtual const void * getLinkedRowResult() = 0;
 };
 
 struct IHThorGraphResults : extends IEclGraphResults

+ 8 - 1
ecl/eclagent/eclagent.ipp

@@ -771,6 +771,7 @@ public:
     virtual const void * queryRow(unsigned whichRow);
     virtual void getLinkedResult(unsigned & count, byte * * & ret);
     virtual const void * getOwnRow(unsigned whichRow);
+    virtual const void * getLinkedRowResult();
 
 protected:
     unsigned id;
@@ -787,6 +788,7 @@ public:
     virtual const void * queryRow(unsigned whichRow);
     virtual void getLinkedResult(unsigned & count, byte * * & ret);
     virtual const void * getOwnRow(unsigned whichRow);
+    virtual const void * getLinkedRowResult();
 
 protected:
     Owned<IEngineRowAllocator> rowsetAllocator;
@@ -819,7 +821,10 @@ public:
     {
         queryResult(id)->getLinkedResult(count, ret);
     }
-
+    virtual const void * getLinkedRowResult(unsigned id)
+    {
+        return queryResult(id)->getLinkedRowResult();
+    }
 protected:
     void ensureAtleast(unsigned id);
 
@@ -976,6 +981,7 @@ public:
 
     virtual void getLinkedResult(unsigned & count, byte * * & ret, unsigned id);
     virtual void getDictionaryResult(size32_t & tcount, byte * * & tgt, unsigned id);
+    virtual const void * getLinkedRowResult(unsigned id);
     inline unsigned __int64 queryId() const
     {
         return id;
@@ -1077,6 +1083,7 @@ public:
 protected:
     IAgentContext * agent;
     CIArrayOf<EclSubGraph> graphs;
+    GraphResults globalResults;
     StringAttr graphName;
     SubGraphMapping subgraphMap;
     Linked<IConstWorkUnit> wu;

+ 21 - 0
ecl/eclagent/eclgraph.cpp

@@ -1085,6 +1085,11 @@ void EclSubGraph::getDictionaryResult(unsigned & count, byte * * & ret, unsigned
     localResults->queryResult(id)->getLinkedResult(count, ret);
 }
 
+const void * EclSubGraph::getLinkedRowResult(unsigned id)
+{
+    return localResults->queryResult(id)->getLinkedRowResult();
+}
+
 
 EclGraphElement * EclSubGraph::idToActivity(unsigned id)
 {
@@ -1310,6 +1315,12 @@ void UninitializedGraphResult::getLinkedResult(unsigned & count, byte * * & ret)
     throw MakeStringException(99, "Graph Result %d accessed before it is created", id);
 }
 
+const void * UninitializedGraphResult::getLinkedRowResult()
+{
+    throw MakeStringException(99, "Graph Result %d accessed before it is created", id);
+}
+
+
 
 void GraphResult::addRowOwn(const void * row)
 {
@@ -1349,6 +1360,14 @@ void GraphResult::getLinkedResult(unsigned & count, byte * * & ret)
     ret = rowset;
 }
 
+const void * GraphResult::getLinkedRowResult()
+{
+    assertex(rows.ordinality() == 1);
+    const void * next = rows.item(0);
+    LinkRoxieRow(next);
+    return next;
+}
+
 //---------------------------------------------------------------------------
 
 GraphResults::GraphResults(unsigned _maxResults)
@@ -1421,6 +1440,8 @@ IThorChildGraph * EclGraph::resolveChildQuery(unsigned subgraphId)
 //NB: resolveLocalQuery (unlike children) can't link otherwise you get a cicular dependency.
 IEclGraphResults * EclGraph::resolveLocalQuery(unsigned subgraphId)
 {
+    if (subgraphId == 0)
+        return &globalResults;
     return idToGraph(subgraphId);
 }
 

+ 9 - 0
ecl/hql/hqlexpr.cpp

@@ -11668,6 +11668,7 @@ extern IHqlExpression *createRow(node_operator op, HqlExprArray & args)
             break;
         }
     case no_getresult:
+    case no_getgraphresult:
         {
             IHqlExpression * record = &args.item(0);
             type = makeRowType(record->getType());
@@ -11675,6 +11676,14 @@ extern IHqlExpression *createRow(node_operator op, HqlExprArray & args)
                 type = makeAttributeModifier(type, getLinkCountedAttr());
             break;
         }
+    case no_readspill:
+        {
+            IHqlExpression * record = queryOriginalRecord(&args.item(0));
+            type = makeRowType(record->getType());
+            if (recordRequiresLinkCount(record))
+                type = makeAttributeModifier(type, getLinkCountedAttr());
+            break;
+        }
     default:
         {
             IHqlExpression * dataset = &args.item(0);

+ 1 - 13
ecl/hql/hqlgram.y

@@ -3850,22 +3850,10 @@ funcRetType
     | propType
     | setType
     | explicitDatasetType
+    | explicitRowType
     | explicitDictionaryType
     | transformType
  // A plain record would be better, but that then causes a s/r error in knownOrUnknownId because scope
-    | ROW '(' recordDef ')'     
-                        {
-                            OwnedHqlExpr expr = $3.getExpr();
-                            $$.setType(makeOriginalModifier(makeRowType(expr->getType()), LINK(expr)));
-                            $$.setPosition($1);
-                        }
-    | LINKCOUNTED ROW '(' recordDef ')'       
-                        {
-                            OwnedHqlExpr expr = $4.getExpr();
-                            Owned<ITypeInfo> rowType = makeOriginalModifier(makeRowType(expr->getType()), LINK(expr));
-                            $$.setType(setLinkCountedAttr(rowType, true));
-                            $$.setPosition($1);
-                        }
     | recordDef         {
                             OwnedHqlExpr expr = $1.getExpr();
 //                          $$.setType(makeOriginalModifier(makeRowType(expr->getType()), LINK(expr)));

+ 4 - 0
ecl/hql/hqlopt.cpp

@@ -201,6 +201,7 @@ void ExpandComplexityMonitor::onExpand(IHqlExpression * select, IHqlExpression *
         case no_null:
         case no_select:
         case no_getresult:
+        case no_getgraphresult:
         case no_id2blob:
             //MORE: Should be a common list somewhere...
             break;
@@ -2396,6 +2397,7 @@ IHqlExpression * CTreeOptimizer::doCreateTransformed(IHqlExpression * transforme
                             case no_select:
                             case no_null:
                             case no_getresult:
+                            case no_getgraphresult:
                                 DBGLOG("Optimizer: Extract value %s from %s", queryNode0Text(match), queryNode1Text(transformed));
                                 noteUnused(child);
                                 return match.getClear();
@@ -2430,6 +2432,7 @@ IHqlExpression * CTreeOptimizer::doCreateTransformed(IHqlExpression * transforme
                                 case no_select:
                                 case no_null:
                                 case no_getresult:
+                                case no_getgraphresult:
                                 case no_inlinetable:
                                 case no_left:
                                 case no_right:
@@ -2475,6 +2478,7 @@ IHqlExpression * CTreeOptimizer::doCreateTransformed(IHqlExpression * transforme
                                 case no_select:
                                 case no_null:
                                 case no_getresult:
+                                case no_getgraphresult:
                                     {
                                         DBGLOG("Optimizer: Extract value %s from %s", queryNode0Text(match), queryNode1Text(transformed));
                                         noteUnused(child);

+ 2 - 2
ecl/hql/hqlutil.cpp

@@ -3617,8 +3617,8 @@ IHqlExpression * createScalarFromGraphResult(ITypeInfo * scalarType, ITypeInfo *
     args.append(*LINK(represents));
     args.append(*getSizetConstant(seq));
     args.append(*createAttribute(rowAtom));
-    OwnedHqlExpr counterResult = createDataset(no_getgraphresult, args);
-    OwnedHqlExpr select = createNewSelectExpr(createRow(no_selectnth, LINK(counterResult), getSizetConstant(1)), LINK(counterField));
+    OwnedHqlExpr counterResult = createRow(no_getgraphresult, args);
+    OwnedHqlExpr select = createNewSelectExpr(LINK(counterResult), LINK(counterField));
     return ensureExprType(select, scalarType);
 }
 

+ 6 - 0
ecl/hqlcpp/hqlcatom.cpp

@@ -25,6 +25,7 @@
 #include "hqlatoms.hpp"
 #include "hqlcatom.hpp"
 
+IAtom * _accessedFromChild_Atom;
 IAtom * activeActivityMarkerAtom;
 IAtom * activeMatchTextAtom;
 IAtom * activeMatchUnicodeAtom;
@@ -328,6 +329,7 @@ IIdAtom * freeExceptionId;
 IIdAtom * getBytesFromBuilderId;
 IIdAtom * getChildQueryDictionaryResultId;
 IIdAtom * getChildQueryLinkedResultId;
+IIdAtom * getChildQueryLinkedRowResultId;
 IIdAtom * getClusterSizeId;
 IIdAtom * getDatasetHashId;
 IIdAtom * getECLId;
@@ -343,6 +345,7 @@ IIdAtom * getLocalDictionaryResultId;
 IIdAtom * getLocalFailMessageId;
 IIdAtom * getLocalFilePositionId;
 IIdAtom * getLocalLinkedResultId;
+IIdAtom * getLocalLinkedRowResultId;
 IIdAtom * getMatchedId;
 IIdAtom * getMatchLengthId;
 IIdAtom * getMatchPositionId;
@@ -971,6 +974,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEID(getBytesFromBuilder);
     MAKEID(getChildQueryDictionaryResult);
     MAKEID(getChildQueryLinkedResult);
+    MAKEID(getChildQueryLinkedRowResult);
     MAKEID(getClusterSize);
     MAKEID(getDatasetHash);
     MAKEID(getECL);
@@ -986,6 +990,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEID(getLocalFailMessage);
     MAKEID(getLocalFilePosition);
     MAKEID(getLocalLinkedResult);
+    MAKEID(getLocalLinkedRowResult);
     MAKEID(getMatched);
     MAKEID(getMatchLength);
     MAKEID(getMatchPosition);
@@ -1460,6 +1465,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEATOM(xmlColumnProvider);
     MAKEATOM(xmlReadMarker);
 
+    MAKESYSATOM(accessedFromChild);
     MAKESYSATOM(conditionalRowMarker);
     MAKESYSATOM(loop);
     MAKESYSATOM(loopFirst);

+ 3 - 0
ecl/hqlcpp/hqlcatom.hpp

@@ -25,6 +25,7 @@
 #endif
 #endif
 
+extern IAtom * _accessedFromChild_Atom;
 extern IAtom * activeActivityMarkerAtom;
 extern IAtom * activeMatchTextAtom;
 extern IAtom * activeMatchUnicodeAtom;
@@ -328,6 +329,7 @@ extern IIdAtom * freeExceptionId;
 extern IIdAtom * getBytesFromBuilderId;
 extern IIdAtom * getChildQueryDictionaryResultId;
 extern IIdAtom * getChildQueryLinkedResultId;
+extern IIdAtom * getChildQueryLinkedRowResultId;
 extern IIdAtom * getClusterSizeId;
 extern IIdAtom * getDatasetHashId;
 extern IIdAtom * getECLId;
@@ -343,6 +345,7 @@ extern IIdAtom * getLocalDictionaryResultId;
 extern IIdAtom * getLocalFailMessageId;
 extern IIdAtom * getLocalFilePositionId;
 extern IIdAtom * getLocalLinkedResultId;
+extern IIdAtom * getLocalLinkedRowResultId;
 extern IIdAtom * getMatchedId;
 extern IIdAtom * getMatchLengthId;
 extern IIdAtom * getMatchPositionId;

+ 32 - 0
ecl/hqlcpp/hqlcpp.cpp

@@ -1733,6 +1733,8 @@ void HqlCppTranslator::cacheOptions()
         DebugOption(options.multiplePersistInstances,"multiplePersistInstances",true),
         DebugOption(options.defaultNumPersistInstances,"defaultNumPersistInstances",-1),
         DebugOption(options.optimizeMax,"optimizeMax",false),
+        DebugOption(options.useResultsForChildSpills,"useResultsForChildSpills",false),
+        DebugOption(options.alwaysUseGraphResults,"alwaysUseGraphResults",false),
     };
 
     //get options values from workunit
@@ -2393,6 +2395,13 @@ void HqlCppTranslator::buildExprAssign(BuildCtx & ctx, const CHqlBoundTarget & t
                 buildExprAssign(ctx, target, aggregate);
                 return;
             }
+            if (shouldEvaluateSelectAsAlias(ctx, expr) && !insideOnStart(ctx))
+            {
+                CHqlBoundExpr temp;
+                doBuildAliasValue(ctx, expr, temp);
+                assign(ctx, target, temp);
+                return;
+            }
             Owned<IReferenceSelector> selector = buildReference(ctx, expr);
             selector->assignTo(ctx, target);
             return;
@@ -2816,6 +2825,24 @@ void HqlCppTranslator::buildAnyExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlB
         buildExpr(ctx, expr, tgt);
 }
 
+bool HqlCppTranslator::shouldEvaluateSelectAsAlias(BuildCtx & ctx, IHqlExpression * expr)
+{
+    //If we're inside an activity that serializes onStart data, then the code to serialize fields
+    //selected from a row are generally simpler than the code to serialize the row itself.
+    if (insideActivityRemoteSerialize(ctx) && !expr->isList())
+    {
+        bool isNew;
+        IHqlExpression * ds = querySelectorDataset(expr, isNew);
+        if (isNew && ds->getOperator() == no_getgraphresult)
+        {
+            IHqlExpression * graphId = ds->queryChild(1);
+            if (isCurrentActiveGraph(ctx, graphId))
+                return true;
+        }
+    }
+    return false;
+}
+
 void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
 {
     node_operator op = expr->getOperator();
@@ -3226,6 +3253,11 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
                 buildExpr(ctx, aggregate, tgt);
                 return;
             }
+            if (shouldEvaluateSelectAsAlias(ctx, expr))
+            {
+                doBuildAliasValue(ctx, expr, tgt);
+                return;
+            }
             Owned<IReferenceSelector> selector = buildReference(ctx, expr);
             selector->get(ctx, tgt);
             return;

+ 5 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -730,6 +730,8 @@ struct HqlCppOptions
     bool                expirePersists;
     bool                actionLinkInNewGraph;
     bool                optimizeMax;
+    bool                useResultsForChildSpills;
+    bool                alwaysUseGraphResults;
 };
 
 //Any information gathered while processing the query should be moved into here, rather than cluttering up the translator class
@@ -950,6 +952,7 @@ public:
     void popMemberFunction();
     unsigned getConsistentUID(IHqlExpression * ptr);
     bool insideOnCreate(BuildCtx & ctx);
+    bool insideOnStart(BuildCtx & ctx);
     bool tempRowRequiresFinalize(IHqlExpression * record) const;
     void convertBoundDatasetToFirstRow(IHqlExpression * expr, CHqlBoundExpr & bound);
     void convertBoundRowToDataset(BuildCtx & ctx, CHqlBoundExpr & bound, const BoundRow * row, ExpressionFormat preferredFormat);
@@ -1688,6 +1691,7 @@ public:
     IHqlExpression * getCurrentActivityId(BuildCtx & ctx);          // can be variable
     void associateSkipReturnMarker(BuildCtx & ctx, IHqlExpression * value, BoundRow * self);
     IHqlExpression * createClearRowCall(BuildCtx & ctx, BoundRow * self);
+    bool insideActivityRemoteSerialize(BuildCtx & ctx);
 
     EvalContext * queryEvalContext(BuildCtx & ctx)          { return (EvalContext *)ctx.queryFirstAssociation(AssocExtractContext); }
     inline unsigned nextActivityId()                        { return ++curActivityId; }
@@ -1746,6 +1750,7 @@ public:
     void filterExpandAssignments(BuildCtx & ctx, TransformBuilder * builder, HqlExprArray & assigns, IHqlExpression * expr);
 
 protected:
+    bool shouldEvaluateSelectAsAlias(BuildCtx & ctx, IHqlExpression * expr);
     IWUResult * createWorkunitResult(int sequence, IHqlExpression * nameExpr);
     void noteFilename(ActivityInstance & instance, const char * name, IHqlExpression * expr, bool isDynamic);
     bool checkGetResultContext(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);

+ 7 - 3
ecl/hqlcpp/hqlcppds.cpp

@@ -4347,6 +4347,7 @@ void HqlCppTranslator::buildRowAssign(BuildCtx & ctx, BoundRow * targetRow, IHql
         //MORE could support no_null, no_if, no_translated, constant no_createrow etc.
         case no_call:
         case no_externalcall:
+        case no_getgraphresult:
             buildExprAssign(ctx, target, expr);
             return;
         case no_comma:
@@ -4960,6 +4961,8 @@ IHqlExpression * HqlCppTranslator::buildGetLocalResult(BuildCtx & ctx, IHqlExpre
         args.append(*LINK(resultNum));
         if (expr->isDictionary())
             return bindFunctionCall(getChildQueryDictionaryResultId, args, exprType);
+        if (expr->isDatarow())
+            return bindFunctionCall(getChildQueryLinkedRowResultId, args, exprType);
         return bindFunctionCall(getChildQueryLinkedResultId, args, exprType);
     }
 
@@ -4982,6 +4985,8 @@ IHqlExpression * HqlCppTranslator::buildGetLocalResult(BuildCtx & ctx, IHqlExpre
     args.append(*LINK(resultNum));
     if (expr->isDictionary())
         return bindFunctionCall(getLocalDictionaryResultId, args, exprType);
+    if (expr->isDatarow())
+        return bindFunctionCall(getLocalLinkedRowResultId, args, exprType);
     return bindFunctionCall(getLocalLinkedResultId, args, exprType);
 }
 
@@ -5019,7 +5024,7 @@ void HqlCppTranslator::doBuildAssignGetGraphResult(BuildCtx & ctx, const CHqlBou
 
 void HqlCppTranslator::doBuildExprGetGraphResult(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format)
 {
-    if (!expr->hasAttribute(externalAtom))
+    if (!expr->hasAttribute(externalAtom) && (!isCurrentActiveGraph(ctx, expr->queryChild(1)) || !insideOnStart(ctx)))
     {
         doBuildAliasValue(ctx, expr, tgt);
         return;
@@ -5035,8 +5040,6 @@ void HqlCppTranslator::doBuildExprGetGraphResult(BuildCtx & ctx, IHqlExpression
     OwnedHqlExpr call = buildGetLocalResult(ctx, expr);
     switch (expr->queryType()->getTypeCode())
     {
-    case type_row:
-        throwUnexpected();
     case type_dictionary:
     case type_table:
     case type_groupedtable:
@@ -5179,6 +5182,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivitySetGraphResult(BuildCtx & ctx,
     }
 
     instance->addAttributeBool("_isSpill", isSpill);
+    instance->addAttributeBool("_fromChild", expr->hasAttribute(_accessedFromChild_Atom));
     if (targetRoxie())
         addGraphIdAttribute(instance, ctx, graphId);
 

+ 4 - 2
ecl/hqlcpp/hqlcppsys.ecl

@@ -599,8 +599,9 @@ const char * cppSystemText[]  = {
     "   executeGraph(const varstring graph, boolean realThor, unsigned4 lenExtract, row parentExtract) : ctxmethod,entrypoint='executeGraph';",
     "   executeChildQueryInstance(unsigned4 lenExtract, row parentExtract)  : method,entrypoint='execute';",
     "   evaluateChildQueryInstance(unsigned4 lenExtract, row parentExtract) : method,entrypoint='evaluate';",       // actually returns something el
-    "   _linkcounted_ dataset getChildQueryLinkedResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getLinkedResult';",
-    "   _linkcounted_ dictionary getChildQueryDictionaryResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getDictionaryResult';",
+    "   linkcounted dataset getChildQueryLinkedResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getLinkedResult';",
+    "   linkcounted dictionary getChildQueryDictionaryResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getDictionaryResult';",
+    "   linkcounted row getChildQueryLinkedRowResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getLinkedRowResult';",
     
     //MORE: Should this be utf8?
     "   varstring getenv(const varstring name, const varstring defaultValue) : pure,ctxmethod,entrypoint='getEnv';",
@@ -750,6 +751,7 @@ const char * cppSystemText[]  = {
 
     "   _linkcounted_ dataset getLocalLinkedResult(unsigned4 id) : method,allocator(false),pure,entrypoint='getLinkedResult';",
     "   linkcounted dictionary getLocalDictionaryResult(unsigned4 id) : method,allocator(false),pure,entrypoint='getDictionaryResult';",
+    "   _linkcounted_ row getLocalLinkedRowResult(unsigned4 id)   : method,allocator(false),pure,entrypoint='getLinkedRowResult';",
     "   unsigned4 getGraphLoopCounter() : ctxmethod,entrypoint='getGraphLoopCounter';",
 
     "   _linkcounted_ row(dummyRecord) finalizeRowClear(unsigned4 _size) : omethod,entrypoint='finalizeRowClear',allocator=false;",

+ 16 - 13
ecl/hqlcpp/hqlhtcpp.cpp

@@ -1381,7 +1381,7 @@ unsigned HqlCppTranslator::cppIndexNextActivity(bool isChildActivity)
 static IHqlExpression * createResultAttribute(IHqlExpression * seq, IHqlExpression * name)
 {
     //if a named user output then set seq to the name so that workunit reads from the named symbol get commoned up correctly
-    if (name && !name->queryType()->isInteger() && seq->queryValue()->getIntValue() >= 0)
+    if (name && !name->queryType()->isInteger() && (getIntValue(seq, -1) >= 0))
         seq = name;
     return createAttribute(resultAtom, LINK(seq), LINK(name));
 }
@@ -1736,7 +1736,6 @@ ActivityInstance::ActivityInstance(HqlCppTranslator & _translator, BuildCtx & ct
     containerActivity = NULL;
     subgraph = queryActiveSubGraph(ctx);
     onCreateStmt = NULL;
-    onCreateMarker = 0;
 
     //count index and count disk need to be swapped to the new (much simpler) mechanism
     //until then, they need to be special cased.
@@ -2250,7 +2249,7 @@ void ActivityInstance::buildPrefix()
 
         evalContext->onCreate.createFunctionStructure(translator, oncreatectx, true, executedRemotely ? "serializeCreateContext" : NULL);
         if (onCreateStmt)
-            onCreateMarker = calcTotalChildren(onCreateStmt);
+            onCreateStmt->finishedFramework();
 
         onstartctx.set(startctx);
 
@@ -2299,10 +2298,6 @@ void ActivityInstance::buildPrefix()
 
 void ActivityInstance::buildSuffix()
 {
-    //If onCreate() doesn't do anything special, then use an implementation in the base
-    if (onCreateStmt && (calcTotalChildren(onCreateStmt) == onCreateMarker))
-        onCreateStmt->setIncluded(false);
-
     //Paranoid check to ensure that library classes aren't used when member functions were required
     if (implementationClassName && (initialGroupMarker != classGroup->numChildren()))
         throwUnexpectedX("Implementation class created, but member functions generated");
@@ -2837,14 +2832,11 @@ void GlobalClassBuilder::buildClass(unsigned priority)
     oncreatectx.addQuoted("ctx = _ctx;");
 
     evalContext->onCreate.createFunctionStructure(translator, oncreatectx, true, NULL);
-    onCreateMarker = calcTotalChildren(onCreateStmt);
+    onCreateStmt->finishedFramework();
 }
 
 void GlobalClassBuilder::completeClass(unsigned priority)
 {
-    if (onCreateStmt && (calcTotalChildren(onCreateStmt) == onCreateMarker))
-        onCreateStmt->setIncluded(false);
-
     //MORE: This should be generated from a system function prototype somehow - so we can extend it to user functions later.
     //arguments and parameters should also be configured similarly.
     if (accessorInterface)
@@ -2936,6 +2928,11 @@ bool HqlCppTranslator::insideOnCreate(BuildCtx & ctx)
     return ctx.queryMatchExpr(insideOnCreateMarker) != NULL;
 }
 
+bool HqlCppTranslator::insideOnStart(BuildCtx & ctx)
+{
+    return ctx.queryMatchExpr(insideOnStartMarker) != NULL;
+}
+
 bool HqlCppTranslator::getInvariantMemberContext(BuildCtx & ctx, BuildCtx * * declarectx, BuildCtx * * initctx, bool isIndependentMaybeShared, bool invariantEachStart)
 {
     EvalContext * instance = queryEvalContext(ctx);
@@ -6306,7 +6303,7 @@ ABoundActivity * HqlCppTranslator::buildActivity(BuildCtx & ctx, IHqlExpression
             case no_datasetfromrow:
                 {
                     OwnedHqlExpr row = expr->cloneAllAnnotations(expr->queryChild(0));  // preserve any position information....
-                    if ((getNumActivityArguments(expr) == 0) && canProcessInline(&ctx, row))
+                    if ((getNumActivityArguments(expr) == 0) && canProcessInline(&ctx, row) && (row->getOperator() != no_getgraphresult))
                         result = doBuildActivityCreateRow(ctx, row, false);
                     else
                         result = buildCachedActivity(ctx, row);
@@ -8898,6 +8895,12 @@ ActivityInstance * HqlCppTranslator::queryCurrentActivity(BuildCtx & ctx)
     return static_cast<ActivityInstance *>(ctx.queryFirstAssociation(AssocActivityInstance));
 }
 
+bool HqlCppTranslator::insideActivityRemoteSerialize(BuildCtx & ctx)
+{
+    ActivityInstance * activeActivity = queryCurrentActivity(ctx);
+    return activeActivity && activeActivity->requiresRemoteSerialize();
+}
+
 unique_id_t HqlCppTranslator::queryCurrentActivityId(BuildCtx & ctx)
 {
     ActivityInstance * activeActivity = queryCurrentActivity(ctx);
@@ -9211,7 +9214,7 @@ IHqlExpression * HqlCppTranslator::getResourcedGraph(IHqlExpression * expr, IHql
 
     checkNormalized(resourced);
 
-    bool createGraphResults = (outputLibraryId != 0);
+    bool createGraphResults = (outputLibraryId != 0) || options.alwaysUseGraphResults;
     resourced.setown(optimizeGraphPostResource(resourced, csfFlags, options.optimizeSpillProject && !createGraphResults));
     if (options.optimizeSpillProject)
     {

+ 1 - 2
ecl/hqlcpp/hqlhtcpp.ipp

@@ -109,7 +109,6 @@ public:
     Owned<EvalContext> parentEvalContext;
     Owned<GlobalClassEvalContext> evalContext;
     IHqlStmt *  onCreateStmt;
-    unsigned    onCreateMarker;
 
     StringAttr className;
     StringAttr baseName;
@@ -167,6 +166,7 @@ public:
     BuildCtx &   onlyEvalOnceContext();
     inline IPropertyTree * querySubgraphNode() { return subgraph ? subgraph->tree.get() : NULL; }
     inline void setImplementationClass(IIdAtom * name) { implementationClassName = name; }
+    inline bool requiresRemoteSerialize() const { return executedRemotely; }
     void setInternalSink(bool value);
 
     void changeActivityKind(ThorActivityKind newKind);
@@ -208,7 +208,6 @@ public:
     Owned<EvalContext> parentEvalContext;
     IHqlStmt *  onCreateStmt;
     IHqlStmt * classGroup;
-    unsigned    onCreateMarker;
     unsigned    initialGroupMarker;
     HqlExprArray constructorArgs;
     HqlExprCopyArray names;

+ 32 - 24
ecl/hqlcpp/hqlinline.cpp

@@ -1025,6 +1025,9 @@ void ParentExtract::beginReuseExtract()
 
 void ParentExtract::beginChildActivity(BuildCtx & declareCtx, BuildCtx & startCtx, GraphLocalisation childLocalisation, IHqlExpression * colocal, bool nested, bool ignoreSelf, ActivityInstance * activityRequiringCast)
 {
+    if (type == PETcallback)
+        assertex(ignoreSelf);
+
     //MORE: If we ever generate grand children - nested classes then the following isn't going to work
     //because accessing colocal->exNNN isn't going to get at the extract defined in the child class+passed to the grandchild.
     //Simplest would be to define the builders in the activity, and access them as activity->exXXX in the (grand)child.
@@ -1039,14 +1042,16 @@ void ParentExtract::beginChildActivity(BuildCtx & declareCtx, BuildCtx & startCt
             if (!colocal && bound != boundExtract.expr)
                 break;
 
+            //If this is the parent extract for a local callback, it will be passed as a parameter, so it shouldn't
+            //be added as a member of the child class
+            if (ignoreSelf && (bound == boundExtract.expr))
+                continue;
+
             declareCtx.addDeclare(bound);
 
             OwnedHqlExpr src;
             if (bound == boundExtract.expr)
             {
-                if (ignoreSelf)
-                    continue;
-
                 //MORE: This cast is a hack.  We should process const correctly in helper functions etc.
                 if (!nested)
                     src.setown(createVariable("(byte *)pe", bound->getType()));
@@ -1669,14 +1674,16 @@ void ClassEvalContext::createMemberAlias(CtxCollection & ctxs, BuildCtx & ctx, I
     ctxs.declarectx.associateExpr(value, tgt);
 }
 
-void ClassEvalContext::doCallNestedHelpers(const char * member, const char * activity)
+void ClassEvalContext::doCallNestedHelpers(const char * member, const char * activity, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)
 {
     StringBuffer s;
 
     onCreate.childctx.addQuoted(s.clear().append(member).append(".onCreate(ctx, ").append(activity).append(");"));
 
-    if (requiresOnStart())
-        onStart.childctx.addQuoted(s.clear().append(member).append(".onStart();"));
+    BuildCtx childctx(onStart.childctx);
+    if (!requiresOnStart())
+        childctx.addConditionalGroup(onStartStmt);
+    childctx.addQuoted(s.clear().append(member).append(".onStart();"));
 }
 
 
@@ -1825,9 +1832,9 @@ void GlobalClassEvalContext::ensureHelpersExist()
 {
 }
 
-void GlobalClassEvalContext::callNestedHelpers(const char * member)
+void GlobalClassEvalContext::callNestedHelpers(const char * member, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)
 {
-    doCallNestedHelpers(member, "this");
+    doCallNestedHelpers(member, "this", onCreateStmt, onStartStmt);
 }
 
 //---------------------------------------------------------------------------
@@ -1849,9 +1856,9 @@ void ActivityEvalContext::ensureHelpersExist()
 }
 
 
-void ActivityEvalContext::callNestedHelpers(const char * member)
+void ActivityEvalContext::callNestedHelpers(const char * member, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)
 {
-    doCallNestedHelpers(member, "this");
+    doCallNestedHelpers(member, "this", onCreateStmt, onStartStmt);
 }
 
 ActivityInstance * ActivityEvalContext::queryActivity()
@@ -1877,6 +1884,7 @@ IHqlExpression * NestedEvalContext::createGraphLookup(unique_id_t id, bool isChi
 
 void NestedEvalContext::ensureHelpersExist()
 {
+    assertex(parentExtract);
     if (!helpersExist)
     {
         if (parent)
@@ -1887,7 +1895,7 @@ void NestedEvalContext::ensureHelpersExist()
 
         //void onStart(ICodeContext * _ctx, <ActivityClass> * _activity)
         BuildCtx oncreatectx(onCreate.declarectx);
-        oncreatectx.addQuotedCompound(s.clear().append("inline void onCreate(ICodeContext * _ctx, ").append(rootActivity->className).append(" * _activity)"));
+        IHqlStmt * onCreateStmt  = oncreatectx.addQuotedCompound(s.clear().append("inline void onCreate(ICodeContext * _ctx, ").append(rootActivity->className).append(" * _activity)"));
         oncreatectx.addQuoted(s.clear().append("activity = _activity;"));
         oncreatectx.addQuoted("ctx = _ctx;");
 
@@ -1899,17 +1907,17 @@ void NestedEvalContext::ensureHelpersExist()
 
         //void onStart(const byte * parentExtract)
         BuildCtx onstartctx(onStart.declarectx);
-        if (requiresOnStart())
-        {
-            onstartctx.addQuotedCompound("inline void onStart()");
-            if (parentExtract)
-                parentExtract->beginChildActivity(onStart.declarectx, onstartctx, GraphCoLocal, colocalMember, true, parentExtract->canSerializeFields(), NULL);
+        IHqlStmt * onStartStmt = onstartctx.addQuotedCompound("inline void onStart()");
+        if (parentExtract)
+            parentExtract->beginChildActivity(onStart.declarectx, onstartctx, GraphCoLocal, colocalMember, true, parentExtract->canSerializeFields(), NULL);
 
-            onstartctx.associateExpr(insideOnStartMarker, NULL);
-            onStart.createFunctionStructure(translator, onstartctx, false, NULL);
-        }
+        onstartctx.associateExpr(insideOnStartMarker, NULL);
+        onStart.createFunctionStructure(translator, onstartctx, false, NULL);
+
+        parent->callNestedHelpers(memberName, onCreateStmt, onStartStmt);
+        if (!requiresOnStart())
+            onStartStmt->finishedFramework();
 
-        parent->callNestedHelpers(memberName);
         helpersExist = true;
     }
 }
@@ -1932,9 +1940,9 @@ bool NestedEvalContext::evaluateInParent(BuildCtx & ctx, IHqlExpression * expr,
     return parent->isRowInvariant(expr) || parentExtract->canEvaluate(expr);
 }
 
-void NestedEvalContext::callNestedHelpers(const char * member)
+void NestedEvalContext::callNestedHelpers(const char * member, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)
 {
-    doCallNestedHelpers(member, "activity");
+    doCallNestedHelpers(member, "activity", onCreateStmt, onStartStmt);
 }
 
 //---------------------------------------------------------------------------
@@ -1947,9 +1955,9 @@ MemberEvalContext::MemberEvalContext(HqlCppTranslator & _translator, ParentExtra
     colocalMember.set(colocalSameClassPreserveExpr);
 }
 
-void MemberEvalContext::callNestedHelpers(const char * member)
+void MemberEvalContext::callNestedHelpers(const char * member, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)
 {
-    parent->callNestedHelpers(member);
+    parent->callNestedHelpers(member, onCreateStmt, onStartStmt);
 }
 
 IHqlExpression * MemberEvalContext::createGraphLookup(unique_id_t id, bool isChild)

+ 7 - 7
ecl/hqlcpp/hqlinline.hpp

@@ -127,7 +127,7 @@ public:
     bool needToEvaluateLocally(BuildCtx & ctx, IHqlExpression * expr);
 
 public://only used by friends
-    virtual void callNestedHelpers(const char * memberName) = 0;
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt) = 0;
     virtual void ensureHelpersExist() = 0;
     virtual bool isRowInvariant(IHqlExpression * expr)      { return false; }
 
@@ -159,7 +159,7 @@ protected:
     void cloneAliasInClass(CtxCollection & ctxs, const CHqlBoundExpr & bound, CHqlBoundExpr & tgt);
     IHqlExpression * cloneExprInClass(CtxCollection & ctxs, IHqlExpression * expr);
     void createMemberAlias(CtxCollection & ctxs, BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt);
-    void doCallNestedHelpers(const char * member, const char * acticity);
+    void doCallNestedHelpers(const char * member, const char * activity, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt);
     void ensureSerialized(CtxCollection & ctxs, const CHqlBoundTarget & tgt, IAtom * serializeForm);
 
 protected:
@@ -173,7 +173,7 @@ class GlobalClassEvalContext : public ClassEvalContext
 public:
     GlobalClassEvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent, BuildCtx & createctx, BuildCtx & startctx);
 
-    virtual void callNestedHelpers(const char * memberName);
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt);
     virtual IHqlExpression * createGraphLookup(unique_id_t id, bool isChild) { throwUnexpected(); }
     virtual void ensureHelpersExist();
     virtual bool isColocal()                                { return false; }
@@ -187,7 +187,7 @@ class ActivityEvalContext : public ClassEvalContext
 public:
     ActivityEvalContext(HqlCppTranslator & _translator, ActivityInstance * _activity, ParentExtract * _parentExtract, EvalContext * _parent, IHqlExpression * _colocal, BuildCtx & createctx, BuildCtx & startctx);
 
-    virtual void callNestedHelpers(const char * memberName);
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt);
     virtual IHqlExpression * createGraphLookup(unique_id_t id, bool isChild);
     virtual void ensureHelpersExist();
     virtual bool isColocal()                                { return (colocalMember != NULL); }
@@ -205,7 +205,7 @@ class NestedEvalContext : public ClassEvalContext
 public:
     NestedEvalContext(HqlCppTranslator & _translator, const char * _memberName, ParentExtract * _parentExtract, EvalContext * _parent, IHqlExpression * _colocal, BuildCtx & createctx, BuildCtx & startctx);
 
-    virtual void callNestedHelpers(const char * memberName);
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt);
     virtual IHqlExpression * createGraphLookup(unique_id_t id, bool isChild);
     virtual void ensureHelpersExist();
 
@@ -226,7 +226,7 @@ class MemberEvalContext : public EvalContext
 public:
     MemberEvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent, BuildCtx & _ctx);
 
-    virtual void callNestedHelpers(const char * memberName);
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt);
     virtual void ensureHelpersExist();
     virtual bool isRowInvariant(IHqlExpression * expr);
 
@@ -257,7 +257,7 @@ public:
     void ensureContextAvailable()                           { }
 
 public:
-    virtual void callNestedHelpers(const char * memberName)     {}
+    virtual void callNestedHelpers(const char * memberName, IHqlStmt * onCreateStmt, IHqlStmt * onStartStmt)     {}
     virtual void ensureHelpersExist()                           {}
 
 protected:

+ 19 - 4
ecl/hqlcpp/hqlresource.cpp

@@ -1206,11 +1206,18 @@ IHqlExpression * ResourcerInfo::createAggregation(IHqlExpression * expr)
 
 bool ResourcerInfo::useGraphResult()
 {
+    if (options->useResultsForChildSpills && linkedFromChild)
+        return true;
+
+    if (options->alwaysUseGraphResults)
+        return true;
+
     if (!options->useGraphResults)
         return false;
 
     if (linkedFromChild)
         return true;
+
     //Roxie converts spills into splitters, so best to retain them
     if (options->targetClusterType == RoxieCluster)
         return false;
@@ -1270,7 +1277,9 @@ IHqlExpression * ResourcerInfo::createSpilledRead(IHqlExpression * spillReason)
             args.append(*createAttribute(_distributed_Atom));
 
         node_operator readOp = spilledDataset ? no_readspill : no_getgraphresult;
-        if (original->isDictionary())
+        if (original->isDatarow())
+            dataset.setown(createRow(readOp, args));
+        else if (original->isDictionary())
             dataset.setown(createDictionary(readOp, args));
         else
             dataset.setown(createDataset(readOp, args));
@@ -1353,6 +1362,8 @@ IHqlExpression * ResourcerInfo::createSpilledWrite(IHqlExpression * transformed)
         args.append(*LINK(options->graphIdExpr));
         args.append(*createSpillName());
         args.append(*createAttribute(_spill_Atom));
+        if (linkedFromChild)
+            args.append(*createAttribute(_accessedFromChild_Atom));
         if (spilledDataset)
             return createValue(no_writespill, makeVoidType(), args);
         return createValue(no_setgraphresult, makeVoidType(), args);
@@ -1819,7 +1830,7 @@ bool ResourcerInfo::isSpilledWrite()
 
 IHqlExpression * ResourcerInfo::wrapRowOwn(IHqlExpression * expr)
 {
-    if (!original->isDataset() && !original->isDictionary())
+    if (!original->isDataset() && !original->isDictionary() && !expr->isDatarow())
         expr = createRow(no_selectnth, expr, getSizetConstant(1));
     return expr;
 }
@@ -1885,6 +1896,7 @@ EclResourcer::EclResourcer(IErrorReceiver * _errors, IConstWorkUnit * _wu, Clust
     spotThroughAggregate = _translatorOptions.spotThroughAggregate && (targetClusterType != RoxieCluster) && (targetClusterType != ThorLCRCluster);
     options.noConditionalLinks = (targetClusterType != HThorCluster);
     options.hoistResourced = _translatorOptions.hoistResourced;
+    options.alwaysUseGraphResults = _translatorOptions.alwaysUseGraphResults;
     options.useGraphResults = false;        // modified by later call
     options.groupedChildIterators = _translatorOptions.groupedChildIterators;
     options.allowSplitBetweenSubGraphs = false;//(targetClusterType == RoxieCluster);
@@ -1898,6 +1910,7 @@ EclResourcer::EclResourcer(IErrorReceiver * _errors, IConstWorkUnit * _wu, Clust
     options.optimizeSharedInputs = _translatorOptions.optimizeSharedGraphInputs && options.combineSiblings;
     options.actionLinkInNewGraph = _translatorOptions.actionLinkInNewGraph || (targetClusterType == HThorCluster);
     options.convertCompoundToExecuteWhen = false;
+    options.useResultsForChildSpills = _translatorOptions.useResultsForChildSpills;// && (targetClusterType != HThorCluster);
 }
 
 EclResourcer::~EclResourcer()               
@@ -4933,7 +4946,7 @@ void EclResourcer::createResourced(ResourceGraphInfo * graph, HqlExprArray & tra
         graph->isDead = true;
     else
     {
-        if (options.useGraphResults)
+        if (options.useGraphResults || options.alwaysUseGraphResults)
             args.append(*createAttribute(childAtom));
         graph->createdGraph.setown(createValue(no_subgraph, makeVoidType(), args));
         transformed.append(*LINK(graph->createdGraph));
@@ -5367,7 +5380,9 @@ IHqlExpression * SpillActivityTransformer::createTransformed(IHqlExpression * ex
                 args.append(*LINK(recordCountAttr));
 
             OwnedHqlExpr ret;
-            if (ds->isDictionary())
+            if (ds->isDatarow())
+                ret.setown(createRow(readOp, args));
+            else if (ds->isDictionary())
                 ret.setown(createDictionary(readOp, args));
             else
                 ret.setown(createDataset(readOp, args));

+ 2 - 0
ecl/hqlcpp/hqlresource.ipp

@@ -65,6 +65,8 @@ public:
     bool     combineSiblings;
     bool     actionLinkInNewGraph;
     bool     convertCompoundToExecuteWhen;
+    bool     useResultsForChildSpills;
+    bool     alwaysUseGraphResults;
 
     IHqlExpression * graphIdExpr;
     unsigned nextResult;

+ 43 - 7
ecl/hqlcpp/hqlstmt.cpp

@@ -35,6 +35,8 @@
 
 #define CLEAR_COPY_THRESHOLD            100
 
+static unsigned doCalcTotalChildren(const IHqlStmt * stmt);
+
 //---------------------------------------------------------------------------
 
 struct HQLCPP_API HqlBoundDefinedValue : public HqlDefinedValue
@@ -198,6 +200,15 @@ IHqlStmt * BuildCtx::addCase(IHqlStmt * _owner, IHqlExpression * source)
 }
 
 
+IHqlStmt * BuildCtx::addConditionalGroup(IHqlStmt * stmt)
+{
+    if (ignoreInput)
+        return NULL;
+    HqlCompoundStmt * next = new HqlConditionalGroupStmt(curStmts, stmt);
+    return appendCompound(next);
+}
+
+
 IHqlStmt * BuildCtx::addContinue()
 {
     if (ignoreInput)
@@ -1038,12 +1049,12 @@ void HqlStmt::addExpr(IHqlExpression * expr)
     exprs.append(*expr);
 }
 
-StmtKind HqlStmt::getStmt()
+StmtKind HqlStmt::getStmt() const
 {
     return (StmtKind)kind;
 }
 
-StringBuffer & HqlStmt::getTextExtra(StringBuffer & out)
+StringBuffer & HqlStmt::getTextExtra(StringBuffer & out) const
 {
     return out;
 }
@@ -1108,7 +1119,7 @@ HqlStmts * HqlStmt::queryContainer()
     return container;
 }
 
-IHqlExpression * HqlStmt::queryExpr(unsigned index)
+IHqlExpression * HqlStmt::queryExpr(unsigned index) const
 {
     if (exprs.isItem(index))
         return &exprs.item(index);
@@ -1124,12 +1135,28 @@ IHqlExpression * HqlStmt::queryExpr(unsigned index)
 #endif
 HqlCompoundStmt::HqlCompoundStmt(StmtKind _kind, HqlStmts * _container) : HqlStmt(_kind, _container), code(this)
 {
+    frameworkCount = 0;
 }
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 
+void HqlCompoundStmt::finishedFramework()
+{
+    frameworkCount = doCalcTotalChildren(this);
+}
+
+
+bool HqlCompoundStmt::isIncluded() const
+{
+    if (!HqlStmt::isIncluded())
+        return false;
+    if (frameworkCount == 0)
+        return true;
+    return frameworkCount != doCalcTotalChildren(this);
+}
+
 void HqlCompoundStmt::mergeScopeWithContainer()
 {
     container->inheritDefinitions(code);
@@ -1148,9 +1175,14 @@ IHqlStmt * HqlCompoundStmt::queryChild(unsigned index) const
 }
 
 
+bool HqlConditionalGroupStmt::isIncluded() const
+{
+    return HqlCompoundStmt::isIncluded() && stmt->isIncluded();
+}
+
 //---------------------------------------------------------------------------
 
-StringBuffer & HqlQuoteStmt::getTextExtra(StringBuffer & out)
+StringBuffer & HqlQuoteStmt::getTextExtra(StringBuffer & out) const
 {
     return out.append(text);
 }
@@ -1904,10 +1936,8 @@ bool RowAssociationIterator::doNext()
 };
 
 
-unsigned calcTotalChildren(IHqlStmt * stmt)
+unsigned doCalcTotalChildren(const IHqlStmt * stmt)
 {
-    if (!stmt->isIncluded())
-        return 0;
     unsigned num = stmt->numChildren();
     unsigned total = 1;
     switch (stmt->getStmt())
@@ -1925,3 +1955,9 @@ unsigned calcTotalChildren(IHqlStmt * stmt)
     return total;
 }
 
+unsigned calcTotalChildren(const IHqlStmt * stmt)
+{
+    if (!stmt->isIncluded())
+        return 0;
+    return doCalcTotalChildren(stmt);
+}

+ 8 - 6
ecl/hqlcpp/hqlstmt.hpp

@@ -97,6 +97,7 @@ public:
     IHqlStmt *                  addBlock();
     IHqlStmt *                  addBreak();
     IHqlStmt *                  addCase(IHqlStmt * owner, IHqlExpression * condition);
+    IHqlStmt *                  addConditionalGroup(IHqlStmt * stmt); // generated if stmt->isIncluded() is true
     IHqlStmt *                  addContinue();
     IHqlStmt *                  addDeclare(IHqlExpression * name, IHqlExpression * value=NULL);
     IHqlStmt *                  addDeclareExternal(IHqlExpression * name);
@@ -199,17 +200,18 @@ enum StmtKind {
 interface IHqlStmt : public IInterface
 {
 public:
-    virtual StringBuffer &  getTextExtra(StringBuffer & out) = 0;
+    virtual StringBuffer &  getTextExtra(StringBuffer & out) const = 0;
     virtual bool            isIncluded() const = 0;
-    virtual StmtKind        getStmt() = 0;
-    virtual unsigned                numChildren() const = 0;
-    virtual IHqlStmt *          queryChild(unsigned index) const = 0;
-    virtual IHqlExpression *queryExpr(unsigned index) = 0;
+    virtual StmtKind        getStmt() const = 0;
+    virtual unsigned        numChildren() const = 0;
+    virtual IHqlStmt *      queryChild(unsigned index) const = 0;
+    virtual IHqlExpression *queryExpr(unsigned index) const = 0;
 
 //used when creating the statement graph
     virtual void            mergeScopeWithContainer() = 0;
     virtual void            setIncomplete(bool incomplete) = 0;
     virtual void            setIncluded(bool _included) = 0;
+    virtual void            finishedFramework() = 0;
 };
 
 class HqlCppTranslator;
@@ -268,7 +270,7 @@ public:
 };
 
 
-unsigned calcTotalChildren(IHqlStmt * stmt);
+unsigned calcTotalChildren(const IHqlStmt * stmt);
 
 IHqlExpression * stripTranslatedCasts(IHqlExpression * e);
 IHqlExpression * peepholeAddExpr(IHqlExpression * left, IHqlExpression * right);

+ 19 - 4
ecl/hqlcpp/hqlstmt.ipp

@@ -29,12 +29,12 @@ class HqlStmt : public CInterfaceOf<IHqlStmt>
 public:
     HqlStmt(StmtKind _kind, HqlStmts * _container);
     
-    virtual StmtKind                getStmt();
-    virtual StringBuffer &          getTextExtra(StringBuffer & out);
+    virtual StmtKind                getStmt() const;
+    virtual StringBuffer &          getTextExtra(StringBuffer & out) const;
     virtual bool                    isIncluded() const;
     virtual unsigned                numChildren() const;
     virtual IHqlStmt *              queryChild(unsigned index) const;
-    virtual IHqlExpression *        queryExpr(unsigned index);
+    virtual IHqlExpression *        queryExpr(unsigned index) const;
     virtual HqlStmts *              queryContainer();
 
             void                    addExpr(IHqlExpression * expr);
@@ -45,6 +45,7 @@ public:
     virtual void                    setIncomplete(bool _incomplete) { incomplete = _incomplete; }
     virtual void                    setIncluded(bool _included) { included = _included; }
             void                    setPriority(unsigned _prio) { priority = _prio; }
+    virtual void                    finishedFramework() { throwUnexpected(); }
 
 protected:
     bool hasChildren() const;
@@ -140,21 +141,35 @@ class HqlCompoundStmt : public HqlStmt
 public:
     HqlCompoundStmt(StmtKind _kind, HqlStmts * _container);
 
+    virtual bool                    isIncluded() const;
     virtual unsigned                numChildren() const;
     virtual void                    mergeScopeWithContainer();
     virtual IHqlStmt *              queryChild(unsigned index) const;
+    virtual void                    finishedFramework();
 
 protected:
     HqlStmts                         code;
+    unsigned                         frameworkCount;
 };
 
 
+class HqlConditionalGroupStmt : public HqlCompoundStmt
+{
+public:
+    HqlConditionalGroupStmt(HqlStmts * _container, IHqlStmt * _stmt) : HqlCompoundStmt(group_stmt, _container), stmt(_stmt) {}
+
+    virtual bool                    isIncluded() const;
+
+protected:
+    IHqlStmt * stmt;
+};
+
 class HqlQuoteStmt : public HqlCompoundStmt
 {
 public:
     HqlQuoteStmt(StmtKind _kind, HqlStmts * _container, const char * _text) : HqlCompoundStmt(_kind, _container), text(_text) {}
 
-    virtual StringBuffer &          getTextExtra(StringBuffer & out);
+    virtual StringBuffer &          getTextExtra(StringBuffer & out) const;
 
 protected:
   StringAttr text;

+ 2 - 0
roxie/ccd/ccdcontext.cpp

@@ -1090,6 +1090,8 @@ public:
     {
         if (queryTraceLevel() > 10)
             CTXLOG("CSlaveContext %p resolveChildGraph %d", this, id);
+        if (id == 0)
+            return graph;
         IActivityGraph *childGraph = childGraphs.getValue(id);
         assertex(childGraph);
         return childGraph;

+ 22 - 0
roxie/ccd/ccdserver.cpp

@@ -5655,6 +5655,18 @@ public:
         countResult = count;
      }
 
+    virtual const void * getLinkedRowResult()
+    {
+        if (!complete)
+            throw MakeStringException(ROXIE_GRAPH_PROCESSING_ERROR, "Internal Error: Reading uninitialised graph result");
+
+        if (count != 1)
+            throw MakeStringException(ROXIE_GRAPH_PROCESSING_ERROR, "Internal Error: Expected a single row result");
+        const void * ret = rowset[0];
+        LinkRoxieRow(ret);
+        return ret;
+    }
+
 //other 
     const void * getRow(unsigned i)
     {
@@ -25211,6 +25223,10 @@ public:
     {
         select(id).getLinkedResult(count, ret);
     }
+    virtual const void * getLinkedRowResult(unsigned id)
+    {
+        return select(id).getLinkedRowResult();
+    }
     void setResult(unsigned id, IGraphResult * result)
     {
         CriticalBlock procedure(cs);
@@ -25488,6 +25504,7 @@ public:
 
     virtual void reset()
     {
+        results.clear();
         ForEachItemIn(idx, sinks)
         {
             IRoxieServerActivity &sink = sinks.item(idx);
@@ -25520,6 +25537,7 @@ public:
 
     virtual void execute()
     {
+        results.setown(new CGraphResults);
         doExecute(0, NULL);
     }
 
@@ -25682,6 +25700,10 @@ public:
     {
         results->getLinkedResult(count, ret, id);
     }
+    virtual const void * getLinkedRowResult(unsigned id)
+    {
+        return results->getLinkedRowResult(id);
+    }
     virtual void setResult(unsigned id, IGraphResult * result)
     {
         results->setResult(id, result);

+ 1 - 0
roxie/ccd/ccdserver.hpp

@@ -215,6 +215,7 @@ interface IGraphResult : public IInterface
 {
     virtual void getLinkedResult(unsigned & countResult, byte * * & result) = 0;
     virtual IRoxieInput * createIterator() = 0;
+    virtual const void * getLinkedRowResult() = 0;
 };
 
 interface IRoxieServerLoopResultProcessor

+ 1 - 0
rtl/include/eclhelper.hpp

@@ -522,6 +522,7 @@ interface IEclGraphResults : public IInterface
 {
     virtual void getLinkedResult(unsigned & count, byte * * & ret, unsigned id) = 0;
     virtual void getDictionaryResult(size32_t & tcount, byte * * & tgt, unsigned id) = 0;
+    virtual const void * getLinkedRowResult(unsigned id) = 0;
 };
 
 //Provided by engine=>can extent

+ 12 - 0
thorlcr/graph/thgraph.cpp

@@ -181,6 +181,12 @@ public:
         }
         result = (byte **)_rowset.getClear();
     }
+    virtual const void * getLinkedRowResult()
+    {
+        assertex(rowStreamCount==1); // catch, just in case
+        Owned<IRowStream> stream = getRowStream();
+        return stream->nextRow();
+    }
 };
 
 /////
@@ -1979,6 +1985,12 @@ void CGraphBase::getLinkedResult(unsigned & count, byte * * & ret, unsigned id)
     result->getLinkedResult(count, ret);
 }
 
+const void * CGraphBase::getLinkedRowResult(unsigned id)
+{
+    Owned<IThorResult> result = getResult(id, true); // will get collated distributed result
+    return result->getLinkedRowResult();
+}
+
 // IThorChildGraph impl.
 IEclGraphResults *CGraphBase::evaluate(unsigned _parentExtractSz, const byte *parentExtract)
 {

+ 9 - 0
thorlcr/graph/thgraph.hpp

@@ -120,6 +120,7 @@ interface IThorResult : extends IInterface
     virtual bool isDistributed() const = 0;
     virtual void serialize(MemoryBuffer &mb) = 0;
     virtual void getLinkedResult(unsigned & count, byte * * & ret) = 0;
+    virtual const void * getLinkedRowResult() = 0;
 };
 
 class CActivityBase;
@@ -696,6 +697,7 @@ public:
 // IEclGraphResults
     virtual void getDictionaryResult(unsigned & count, byte * * & ret, unsigned id);
     virtual void getLinkedResult(unsigned & count, byte * * & ret, unsigned id);
+    virtual const void * getLinkedRowResult(unsigned id);
 
 // IThorChildGraph
 //  virtual void getResult(size32_t & retSize, void * & ret, unsigned id);
@@ -1034,6 +1036,7 @@ protected:
         virtual void getResult(size32_t & retSize, void * & ret) { throw MakeStringException(0, "Graph Result %d accessed before it is created", id); }
         virtual void getLinkedResult(unsigned & count, byte * * & ret) { throw MakeStringException(0, "Graph Result %d accessed before it is created", id); }
         virtual void getDictionaryResult(unsigned & count, byte * * & ret) { throw MakeStringException(0, "Graph Result %d accessed before it is created", id); }
+        virtual const void * getLinkedRowResult() { throw MakeStringException(0, "Graph Result %d accessed before it is created", id); }
     };
     IArrayOf<IThorResult> results;
     CriticalSection cs;
@@ -1091,6 +1094,12 @@ public:
         Owned<IThorResult> result = getResult(id, true);
         result->getLinkedResult(count, ret);
     }
+    virtual const void * getLinkedRowResult(unsigned id)
+    {
+        Owned<IThorResult> result = getResult(id, true);
+        return result->getLinkedRowResult();
+    }
+
     virtual void setOwner(activity_id _ownerId) { ownerId = _ownerId; }
     virtual activity_id queryOwnerId() const { return ownerId; }
 };

+ 6 - 1
thorlcr/graph/thgraphmaster.cpp

@@ -1988,7 +1988,12 @@ public:
     virtual void getLinkedResult(unsigned & count, byte * * & ret)
     {
         ensure();
-        return result->getLinkedResult(count, ret);
+        result->getLinkedResult(count, ret);
+    }
+    virtual const void * getLinkedRowResult()
+    {
+        ensure();
+        return result->getLinkedRowResult();
     }
 };