Przeglądaj źródła

Merge pull request #3720 from ghalliday/issue2351

HPCC-2351 Ensure APPLY actions are executed in the correct order

Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 12 lat temu
rodzic
commit
505235a858

+ 5 - 0
ecl/hql/hqlutil.cpp

@@ -4198,6 +4198,11 @@ IHqlExpression * inheritAttribute(IHqlExpression * expr, IHqlExpression * donor,
     return appendOwnedOperand(expr, LINK(donor->queryProperty(name)));
 }
 
+IHqlExpression * appendAttribute(IHqlExpression * expr, _ATOM attr)
+{
+    return appendOwnedOperand(expr, createAttribute(attr));
+}
+
 IHqlExpression * appendOwnedOperand(IHqlExpression * expr, IHqlExpression * ownedOperand)
 {
     if (!ownedOperand)

+ 1 - 0
ecl/hql/hqlutil.hpp

@@ -152,6 +152,7 @@ extern HQL_API IHqlExpression * removeLocalAttribute(IHqlExpression * expr);
 extern HQL_API IHqlExpression * removeProperty(IHqlExpression * expr, _ATOM attr);
 extern HQL_API IHqlExpression * removeOperand(IHqlExpression * expr, IHqlExpression * operand);
 extern HQL_API IHqlExpression * removeChildOp(IHqlExpression * expr, node_operator op);
+extern HQL_API IHqlExpression * appendAttribute(IHqlExpression * expr, _ATOM attr);
 extern HQL_API IHqlExpression * appendOwnedOperand(IHqlExpression * expr, IHqlExpression * ownedOperand);
 extern HQL_API IHqlExpression * replaceOwnedProperty(IHqlExpression * expr, IHqlExpression * ownedProeprty);
 extern HQL_API IHqlExpression * appendOwnedOperandsF(IHqlExpression * expr, ...);

+ 1 - 1
ecl/hqlcpp/hqlcpp.ipp

@@ -1810,7 +1810,7 @@ protected:
     double getComplexity(IHqlExpression * expr, ClusterType cluster);
     bool prepareToGenerate(HqlQueryContext & query, WorkflowArray & exprs, bool isEmbeddedLibrary);
     IHqlExpression * getResourcedGraph(IHqlExpression * expr, IHqlExpression * graphIdExpr);
-    IHqlExpression * getResourcedChildGraph(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * graphIdExpr, unsigned numResults, node_operator graphKind);
+    IHqlExpression * getResourcedChildGraph(BuildCtx & ctx, IHqlExpression * childQuery, unsigned numResults, node_operator graphKind);
     IHqlExpression * optimizeCompoundSource(IHqlExpression * expr, unsigned flags);
     IHqlExpression * optimizeGraphPostResource(IHqlExpression * expr, unsigned csfFlags);
     bool isInlineOk();

+ 19 - 18
ecl/hqlcpp/hqlcppds.cpp

@@ -1260,12 +1260,14 @@ unsigned ChildGraphExprBuilder::addInput()
     return id;
 }
 
-IHqlExpression * ChildGraphExprBuilder::getGraph()
+IHqlExpression * ChildGraphExprBuilder::getGraph(_ATOM extraAttrName)
 {
     HqlExprArray args;
     args.append(*LINK(represents));
     args.append(*getSizetConstant(numResults()));
     args.append(*createActionList(results));
+    if (extraAttrName)
+        args.append(*createAttribute(extraAttrName));
     return createValue(no_childquery, makeVoidType(), args);
 }
 
@@ -1273,7 +1275,7 @@ IHqlExpression * ChildGraphExprBuilder::getGraph()
 // Child dataset processing
 
 ChildGraphBuilder::ChildGraphBuilder(HqlCppTranslator & _translator, IHqlExpression * subgraph)
-: translator(_translator)
+: translator(_translator), childQuery(subgraph)
 {
     represents.set(subgraph->queryChild(0));
     id = translator.nextActivityId();
@@ -1285,9 +1287,6 @@ ChildGraphBuilder::ChildGraphBuilder(HqlCppTranslator & _translator, IHqlExpress
     StringBuffer s;
     resultInstanceExpr.setown(createQuoted(appendUniqueId(s.append("res"), id), makeBoolType()));
     numResults = (unsigned)getIntValue(subgraph->queryChild(1));
-
-    IHqlExpression * actions = subgraph->queryChild(2);
-    actions->unwindList(results, no_actionlist);
 }
 
 void ChildGraphBuilder::generateGraph(BuildCtx & ctx)
@@ -1298,8 +1297,8 @@ void ChildGraphBuilder::generateGraph(BuildCtx & ctx)
     //Remove this line once all engines use the new child queries exclusively
     if (numResults == 0) numResults++;
 
-    OwnedHqlExpr query = createActionList(results);
-    OwnedHqlExpr resourced = translator.getResourcedChildGraph(graphctx, query, represents, numResults, no_none);
+    IHqlExpression * query = childQuery->queryChild(2);
+    OwnedHqlExpr resourced = translator.getResourcedChildGraph(graphctx, childQuery, numResults, no_none);
 
     Owned<ParentExtract> extractBuilder = translator.createExtractBuilder(graphctx, PETchild, represents, resourced, true);
     if (!translator.queryOptions().serializeRowsetInExtract)
@@ -1347,8 +1346,8 @@ void ChildGraphBuilder::generatePrefetchGraph(BuildCtx & _ctx, OwnedHqlExpr * re
     BuildCtx aliasctx(ctx);
     aliasctx.addGroup();
 
-    OwnedHqlExpr query = createActionList(results);
-    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_none);
+    IHqlExpression * query = childQuery->queryChild(2);
+    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, childQuery, numResults, no_none);
 
     Owned<ParentExtract> extractBuilder = translator.createExtractBuilder(ctx, PETchild, represents, resourced, false);
     createBuilderAlias(aliasctx, extractBuilder);
@@ -1379,8 +1378,8 @@ unique_id_t ChildGraphBuilder::buildLoopBody(BuildCtx & ctx, bool multiInstance)
     BuildCtx subctx(ctx);
     subctx.addGroup();
 
-    OwnedHqlExpr query = createActionList(results);
-    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_loop);
+    IHqlExpression * query = childQuery->queryChild(2);
+    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, childQuery, numResults, no_loop);
     //Add a flag to indicate multi instance
     if (multiInstance)
         resourced.setown(appendOwnedOperand(resourced, createAttribute(multiInstanceAtom)));
@@ -1481,9 +1480,9 @@ unique_id_t ChildGraphBuilder::buildGraphLoopBody(BuildCtx & ctx, bool isParalle
     BuildCtx subctx(ctx);
     subctx.addGroup();
 
-    OwnedHqlExpr query = createActionList(results);
+    IHqlExpression * query = childQuery->queryChild(2);
     translator.traceExpression("Before Loop resource", query);
-    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_loop);
+    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, childQuery, numResults, no_loop);
     translator.traceExpression("After Loop resource", resourced);
 
     //Add a flag to indicate multi instance
@@ -1514,8 +1513,8 @@ unique_id_t ChildGraphBuilder::buildRemoteGraph(BuildCtx & ctx)
     BuildCtx subctx(ctx);
     subctx.addGroup();
 
-    OwnedHqlExpr query = createActionList(results);
-    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_allnodes);
+    IHqlExpression * query = childQuery->queryChild(2);
+    OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, childQuery, numResults, no_allnodes);
 
     Owned<ParentExtract> extractBuilder = translator.createExtractBuilder(ctx, PETremote, represents, GraphRemote, false);
 
@@ -1576,12 +1575,13 @@ void HqlCppTranslator::buildAssignChildDataset(BuildCtx & ctx, const CHqlBoundTa
 }
 
 
-IHqlExpression * HqlCppTranslator::getResourcedChildGraph(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * graphIdExpr, unsigned numResults, node_operator graphKind)
+IHqlExpression * HqlCppTranslator::getResourcedChildGraph(BuildCtx & ctx, IHqlExpression * childQuery, unsigned numResults, node_operator graphKind)
 {
     if (options.paranoidCheckNormalized || options.paranoidCheckDependencies)
         DBGLOG("Before resourcing a child graph");
 
-    LinkedHqlExpr resourced = expr;
+    IHqlExpression * graphIdExpr = childQuery->queryChild(0);
+    LinkedHqlExpr resourced = childQuery->queryChild(2);
     checkNormalized(ctx, resourced);
 
     unsigned csfFlags = CSFindex|options.optimizeDiskFlag;
@@ -1626,7 +1626,7 @@ IHqlExpression * HqlCppTranslator::getResourcedChildGraph(BuildCtx & ctx, IHqlEx
         resourced.setown(resourceLoopGraph(*this, activeRows, resourced, targetClusterType, graphIdExpr, &numResults, insideChild));
     }
     else
-        resourced.setown(resourceNewChildGraph(*this, activeRows, resourced, targetClusterType, graphIdExpr, &numResults));
+        resourced.setown(resourceNewChildGraph(*this, activeRows, resourced, targetClusterType, graphIdExpr, &numResults, childQuery->hasProperty(sequentialAtom)));
 
     DEBUG_TIMER("EclServer: resource graph", msTick()-time);
     checkNormalized(ctx, resourced);
@@ -1664,6 +1664,7 @@ IHqlExpression * HqlCppTranslator::getResourcedChildGraph(BuildCtx & ctx, IHqlEx
     if (options.paranoidCheckNormalized || options.paranoidCheckDependencies)
         DBGLOG("After resourcing a child graph");
 
+    resourced.setown(inheritAttribute(resourced, childQuery, sequentialAtom));
     return resourced.getClear();
 }
 

+ 2 - 2
ecl/hqlcpp/hqlcppds.hpp

@@ -27,7 +27,7 @@ public:
     IHqlExpression * addDataset(IHqlExpression * expr);
     void addAction(IHqlExpression * expr);
     unsigned addInput();
-    IHqlExpression * getGraph();
+    IHqlExpression * getGraph(_ATOM extraAttrName = NULL);
 
     inline IHqlExpression * queryRepresents() const { return represents; }
     inline unsigned numResults() const { return numInputs + numOutputs; }
@@ -61,11 +61,11 @@ protected:
     HqlCppTranslator & translator;
     unsigned id;
     StringBuffer instanceName;
+    LinkedHqlExpr childQuery;
     OwnedHqlExpr instanceExpr;
     OwnedHqlExpr resultInstanceExpr;
     OwnedHqlExpr represents;
     OwnedHqlExpr resultsExpr;
-    HqlExprArray results;
     unsigned numResults;
 };
 

+ 3 - 1
ecl/hqlcpp/hqlhtcpp.cpp

@@ -8530,7 +8530,7 @@ public:
     {
         if (builder)
         {
-            OwnedHqlExpr childquery = builder->getGraph();
+            OwnedHqlExpr childquery = builder->getGraph(sequentialAtom);
             translator.buildStmt(ctx, childquery);
             builder.clear();
         }
@@ -8764,6 +8764,8 @@ unsigned HqlCppTranslator::doBuildThorChildSubGraph(BuildCtx & ctx, IHqlExpressi
         subGraph->setPropBool("@delayed", true);
     if (expr->queryProperty(childAtom))
         subGraph->setPropBool("@child", true);
+    if (expr->hasProperty(sequentialAtom))
+        subGraph->setPropBool("@sequential", true);
 
     if (insideChildGraph(ctx))
     {

+ 22 - 5
ecl/hqlcpp/hqlresource.cpp

@@ -654,6 +654,7 @@ ResourceGraphInfo::ResourceGraphInfo(CResourceOptions * _options) : resources(_o
     isUnconditional = false;
     mergedConditionSource = false;
     hasConditionSource = false;
+    hasSequentialSource = false;
     isDead = false;
     startedGeneratingResourced = false;
     inheritedExpandedDependencies = false;
@@ -901,6 +902,9 @@ bool ResourceGraphInfo::mergeInSource(ResourceGraphInfo & other, const CResource
     if (options->checkResources() && !allocateResources(other.resources, limit))
         return false;
 
+    if (hasSequentialSource && other.hasSequentialSource)
+        return false;
+
     mergeGraph(other, isConditionalLink, mergeConditions);
     return true;
 }
@@ -917,6 +921,12 @@ void ResourceGraphInfo::mergeGraph(ResourceGraphInfo & other, bool isConditional
     if (other.hasConditionSource)
         hasConditionSource = true;
 
+    if (other.hasSequentialSource)
+    {
+        assertex(!hasSequentialSource);
+        hasSequentialSource = true;
+    }
+
     //Recalculate the dependents, because sources of the source merged in may no longer be indirect
     //although they may be via another path.  
     options->noteGraphsChanged();
@@ -949,6 +959,9 @@ bool ResourceGraphInfo::mergeInSibling(ResourceGraphInfo & other, const CResourc
     if ((!isUnconditional || !other.isUnconditional) && !hasSameConditions(other))
         return false;
 
+    if (hasSequentialSource && other.hasSequentialSource)
+        return false;
+
     if (isDependentOn(other, false) || other.isDependentOn(*this, false))
         return false;
 
@@ -1735,6 +1748,7 @@ EclResourcer::EclResourcer(IErrorReceiver * _errors, IConstWorkUnit * _wu, Clust
     targetClusterType = _targetClusterType; 
     clusterSize = _clusterSize ? _clusterSize : FIXED_CLUSTER_SIZE;
     insideNeverSplit = false;
+    sequential = false;
     options.mangleSpillNameWithWuid = false;
     options.minimizeSpillSize = _translatorOptions.minimizeSpillSize;
 
@@ -2806,6 +2820,8 @@ void EclResourcer::createInitialGraph(IHqlExpression * expr, IHqlExpression * ow
             thisGraph.setown(createGraph());
             connectGraphs(thisGraph, expr, ownerGraph, owner, linkKind);
             info->numExternalUses++;
+            if (!ownerGraph && sequential)
+                thisGraph->hasSequentialSource = true;
         }
         info->graph.set(thisGraph);
 
@@ -4743,7 +4759,7 @@ IHqlExpression * resourceThorGraph(HqlCppTranslator & translator, IHqlExpression
 
 static IHqlExpression * doResourceGraph(HqlCppTranslator & translator, HqlExprCopyArray * activeRows, IHqlExpression * expr, 
                                         ClusterType targetClusterType, unsigned clusterSize,
-                                        IHqlExpression * graphIdExpr, unsigned * numResults, bool isChild, bool useGraphResults)
+                                        IHqlExpression * graphIdExpr, unsigned * numResults, bool isChild, bool useGraphResults, bool sequential)
 {
     HqlExprArray transformed;
     {
@@ -4752,6 +4768,7 @@ static IHqlExpression * doResourceGraph(HqlCppTranslator & translator, HqlExprCo
             resourcer.setChildQuery(true);
         resourcer.setNewChildQuery(graphIdExpr, *numResults);
         resourcer.setUseGraphResults(useGraphResults);
+        resourcer.setSequential(sequential);
 
         if (activeRows)
             resourcer.tagActiveCursors(*activeRows);
@@ -4769,18 +4786,18 @@ static IHqlExpression * doResourceGraph(HqlCppTranslator & translator, HqlExprCo
 
 IHqlExpression * resourceLibraryGraph(HqlCppTranslator & translator, IHqlExpression * expr, ClusterType targetClusterType, unsigned clusterSize, IHqlExpression * graphIdExpr, unsigned * numResults)
 {
-    return doResourceGraph(translator, NULL, expr, targetClusterType, clusterSize, graphIdExpr, numResults, false, true);       //?? what value for isChild (e.g., thor library call).  Need to gen twice?
+    return doResourceGraph(translator, NULL, expr, targetClusterType, clusterSize, graphIdExpr, numResults, false, true, false);       //?? what value for isChild (e.g., thor library call).  Need to gen twice?
 }
 
 
-IHqlExpression * resourceNewChildGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults)
+IHqlExpression * resourceNewChildGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults, bool sequential)
 {
-    return doResourceGraph(translator, &activeRows, expr, targetClusterType, 0, graphIdExpr, numResults, true, true);
+    return doResourceGraph(translator, &activeRows, expr, targetClusterType, 0, graphIdExpr, numResults, true, true, sequential);
 }
 
 IHqlExpression * resourceLoopGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults, bool insideChildQuery)
 {
-    return doResourceGraph(translator, &activeRows, expr, targetClusterType, 0, graphIdExpr, numResults, insideChildQuery, true);
+    return doResourceGraph(translator, &activeRows, expr, targetClusterType, 0, graphIdExpr, numResults, insideChildQuery, true, false);
 }
 
 IHqlExpression * resourceRemoteGraph(HqlCppTranslator & translator, IHqlExpression * expr, ClusterType targetClusterType, unsigned clusterSize)

+ 1 - 1
ecl/hqlcpp/hqlresource.hpp

@@ -24,7 +24,7 @@
 IHqlExpression * resourceThorGraph(HqlCppTranslator & translator, IHqlExpression * expr, ClusterType targetClusterType, unsigned clusterSize, IHqlExpression * graphIdExpr);
 IHqlExpression * resourceLibraryGraph(HqlCppTranslator & translator, IHqlExpression * expr, ClusterType targetClusterType, unsigned clusterSize, IHqlExpression * graphIdExpr, unsigned * numResults);
 IHqlExpression * resourceLoopGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults, bool insideChildQuery);
-IHqlExpression * resourceNewChildGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults);
+IHqlExpression * resourceNewChildGraph(HqlCppTranslator & translator, HqlExprCopyArray & activeRows, IHqlExpression * expr, ClusterType targetClusterType, IHqlExpression * graphIdExpr, unsigned * numResults, bool sequential);
 IHqlExpression * resourceRemoteGraph(HqlCppTranslator & translator, IHqlExpression * expr, ClusterType targetClusterType, unsigned clusterSize);
 
 IHqlExpression * convertSpillsToActivities(IHqlExpression * expr);

+ 10 - 7
ecl/hqlcpp/hqlresource.ipp

@@ -190,13 +190,14 @@ public:
     CResources resources;
     unsigned depth;
     unsigned depthSequence;
-    bool beenResourced;
-    bool isUnconditional;
-    bool mergedConditionSource;
-    bool hasConditionSource;
-    bool isDead;
-    bool startedGeneratingResourced;
-    bool inheritedExpandedDependencies;
+    bool beenResourced:1;
+    bool isUnconditional:1;
+    bool mergedConditionSource:1;
+    bool hasConditionSource:1;
+    bool hasSequentialSource:1;
+    bool isDead:1;
+    bool startedGeneratingResourced:1;
+    bool inheritedExpandedDependencies:1;
     struct
     {
         ResourceGraphInfo * other;
@@ -313,6 +314,7 @@ public:
     void resourceRemoteGraph(HqlExprArray & exprs, HqlExprArray & transformed);
     void setChildQuery(bool value);
     void setNewChildQuery(IHqlExpression * graphIdExpr, unsigned numResults);
+    void setSequential(bool _sequential) { sequential = _sequential; }
     void setUseGraphResults(bool _useGraphResults) 
     { 
         options.useGraphResults = _useGraphResults; 
@@ -422,6 +424,7 @@ protected:
     bool spillMultiCondition;
     bool spotThroughAggregate;
     bool insideNeverSplit;
+    bool sequential;
     CResourceOptions options;
     HqlExprArray rootConditions;
     HqlExprCopyArray activeSelectors;

+ 5 - 3
roxie/ccd/ccdquery.cpp

@@ -595,7 +595,7 @@ protected:
     ActivityArray *loadChildGraph(IPropertyTree &graph)
     {
         // MORE - this is starting to look very much like loadGraph (on Roxie server side)
-        ActivityArray *activities = new ActivityArray(true, graph.getPropBool("@delayed"), graph.getPropBool("@library"));
+        ActivityArray *activities = new ActivityArray(true, graph.getPropBool("@delayed"), graph.getPropBool("@library"), graph.getPropBool("@sequential"));
         unsigned subgraphId = graph.getPropInt("@id");
         try
         {
@@ -1212,7 +1212,8 @@ public:
     virtual ActivityArray *loadGraph(IPropertyTree &graph, const char *graphName)
     {
         bool isLibraryGraph = graph.getPropBool("@library");
-        ActivityArray *activities = new ActivityArray(isLibraryGraph, false, isLibraryGraph);
+        bool isSequential = graph.getPropBool("@sequential");
+        ActivityArray *activities = new ActivityArray(isLibraryGraph, false, isLibraryGraph, isSequential);
         if (isLibraryGraph)
             activities->setLibraryGraphId(graph.getPropInt("node/@id"));
         try
@@ -1522,7 +1523,8 @@ public:
     {
         // MORE: common up with loadGraph for the Roxie server..
         bool isLibraryGraph = graph.getPropBool("@library");
-        ActivityArray *activities = new ActivityArray(isLibraryGraph, false, isLibraryGraph);
+        bool isSequential = graph.getPropBool("@sequential");
+        ActivityArray *activities = new ActivityArray(isLibraryGraph, false, isLibraryGraph, isSequential);
         if (isLibraryGraph)
             activities->setLibraryGraphId(graph.getPropInt("node/@id"));
         try

+ 7 - 1
roxie/ccd/ccdquery.hpp

@@ -123,10 +123,15 @@ class ActivityArray : public CInterface
     bool multiInstance;
     bool delayed;
     bool library;
+    bool sequential;
     unsigned libraryGraphId;
 
 public:
-    ActivityArray(bool _multiInstance, bool _delayed, bool _library) { multiInstance = _multiInstance; delayed = _delayed; library = _library; libraryGraphId = 0; }
+    ActivityArray(bool _multiInstance, bool _delayed, bool _library, bool _sequential)
+     : multiInstance(_multiInstance), delayed(_delayed), library(_library), sequential(_sequential)
+    {
+        libraryGraphId = 0;
+    }
 
     unsigned findActivityIndex(unsigned id);
     unsigned recursiveFindActivityIndex(unsigned id);
@@ -139,6 +144,7 @@ public:
     inline bool isMultiInstance() const { return multiInstance; }
     inline bool isDelayed() const { return delayed; }
     inline bool isLibrary() const { return library; }
+    inline bool isSequential() const { return sequential; }
     inline unsigned getLibraryGraphId() const { return libraryGraphId; }
 };
 MAKEPointerArray(ActivityArray, ActivityArrayArray);

+ 2 - 2
roxie/ccd/ccdserver.cpp

@@ -26935,7 +26935,7 @@ public:
         if (sinks.ordinality()==1)
             sinks.item(0).execute(parentExtractSize, parentExtract);
 #ifdef PARALLEL_EXECUTE
-        else if (!probeManager)
+        else if (!probeManager && !graphDefinition.isSequential())
         {
             class casyncfor: public CAsyncFor
             {
@@ -32746,7 +32746,7 @@ protected:
         DBGLOG("testPrefetchProject");
         init();
         Owned <IRoxieServerActivityFactory> factory = createRoxieServerPrefetchProjectActivityFactory(1, 1, *queryFactory, prefetchProjectActivityTestFactory, TAKprefetchproject);
-        Owned<ActivityArray> childGraph = new ActivityArray(false, false, false);
+        Owned<ActivityArray> childGraph = new ActivityArray(false, false, false, false);
         IRoxieServerActivityFactory *ttf = createRoxieServerTempTableActivityFactory(2, 1, *queryFactory, tempTableActivityTestFactory, TAKtemptable);
         IRoxieServerActivityFactory *snf = createRoxieServerSelectNActivityFactory(3, 1, *queryFactory, selectNActivityTestFactory, TAKselectn);
         IRoxieServerActivityFactory *lrf = createRoxieServerLocalResultWriteActivityFactory(4, 1, *queryFactory, localResultActivityTestFactory, TAKlocalresultwrite, 0, 8, true);

+ 39 - 0
testing/ecl/apply3.ecl

@@ -0,0 +1,39 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+r := { unsigned id; };
+o := { string10 forename; unsigned id; };
+
+namesRecord :=  RECORD
+ string20       surname;
+ string10       forename;
+ dataset(r)     ids;
+END;
+
+namesTable := dataset([
+        {'Hawthorn','Gavin',[{1},{2},{3}]},
+        {'Hawthorn','Mia',[{4},{5},{8}]},
+        {'Smithe','Pru',[{11}]},
+        {'X','Z',[{10}]}], namesRecord);
+
+f := namesTable.ids(id & 1 = 1);
+
+apply(namesTable,
+    output(PROJECT(f, TRANSFORM(o, SELF.id := LEFT.id; SELF.forename := namesTable.forename)),named('Result'),EXTEND),
+    output(PROJECT(f, TRANSFORM(o, SELF.id := LEFT.id * 3; SELF.forename := namesTable.forename)),named('Result'),EXTEND)
+    );
+

+ 0 - 1
testing/ecl/applyaction.ecl

@@ -16,7 +16,6 @@
 ############################################################################## */
 
 //nothor
-//skip type==roxie TBD
 
 fibRecord := 
             RECORD

+ 10 - 0
testing/ecl/key/apply3.xml

@@ -0,0 +1,10 @@
+<Dataset name='Result'>
+ <Row><forename>Gavin     </forename><id>1</id></Row>
+ <Row><forename>Gavin     </forename><id>3</id></Row>
+ <Row><forename>Gavin     </forename><id>3</id></Row>
+ <Row><forename>Gavin     </forename><id>9</id></Row>
+ <Row><forename>Mia       </forename><id>5</id></Row>
+ <Row><forename>Mia       </forename><id>15</id></Row>
+ <Row><forename>Pru       </forename><id>11</id></Row>
+ <Row><forename>Pru       </forename><id>33</id></Row>
+</Dataset>