浏览代码

Initial work on optimizing nested ifs and aliases

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

+ 18 - 2
ecl/hql/hqlexpr.cpp

@@ -424,6 +424,18 @@ extern HQL_API IHqlExpression * cloneInheritedAnnotations(IHqlExpression * donor
     }
 }
 
+IHqlExpression * cloneMissingAnnotations(IHqlExpression * donor, IHqlExpression * body)
+{
+    annotate_kind kind = donor->getAnnotationKind();
+    if (kind == annotate_none)
+        return LINK(body);
+
+    OwnedHqlExpr newbody = cloneMissingAnnotations(donor->queryBody(true), body);
+    if (queryAnnotation(newbody, kind))
+        return newbody.getClear();
+    return donor->cloneAnnotation(newbody);
+}
+
 extern HQL_API IHqlExpression * forceCloneSymbol(IHqlExpression * donor, IHqlExpression * expr)
 {
     OwnedHqlExpr result = cloneAnnotationKind(donor, expr, annotate_symbol);
@@ -519,13 +531,17 @@ extern HQL_API void gatherMetaProperties(HqlExprCopyArray & matches, _ATOM searc
 
 extern HQL_API IHqlExpression * queryLocation(IHqlExpression * expr)
 {
+    IHqlExpression * best = NULL;
     loop
     {
         annotate_kind kind = expr->getAnnotationKind();
         if (kind == annotate_none)
-            return NULL;
-        if (kind == annotate_location || kind == annotate_symbol)
+            return best;
+        if (kind == annotate_location)
+            return expr;
+        if (kind == annotate_symbol)
             return expr;
+//            best = expr;
         expr = expr->queryBody(true);
     }
 }

+ 2 - 1
ecl/hql/hqlexpr.hpp

@@ -71,7 +71,7 @@ extern HQL_API ISourcePath * createSourcePath(const char * name);
 extern HQL_API ISourcePath * createSourcePath(unsigned len, const char * name);
 
 class HqlGramCtx;       // opaque, but still not very nice
-class HqlExprArray : public IArrayOf<IHqlExpression>
+class HQL_API HqlExprArray : public IArrayOf<IHqlExpression>
 {
 public:
     void swapWith(HqlExprArray & other);
@@ -1438,6 +1438,7 @@ extern HQL_API bool okToAddLocation(IHqlExpression *);
 
 extern HQL_API IHqlExpression * cloneAnnotationKind(IHqlExpression * donor, IHqlExpression * expr, annotate_kind search);
 extern HQL_API IHqlExpression * cloneInheritedAnnotations(IHqlExpression * donor, IHqlExpression * expr);
+extern HQL_API IHqlExpression * cloneMissingAnnotations(IHqlExpression * donor, IHqlExpression * body);
 extern HQL_API IHqlExpression * forceCloneSymbol(IHqlExpression * donor, IHqlExpression * expr);
 
 // donor must be a symbol.  Any of the other arguments can be null to inherit the existing values

+ 45 - 0
ecl/hql/hqlutil.cpp

@@ -2760,6 +2760,32 @@ IHqlExpression * getInverse(IHqlExpression * op)
     return createValue(no_not, makeBoolType(), LINK(op));
 }
 
+IHqlExpression * getNormalizedCondition(IHqlExpression * expr)
+{
+    if (expr->getOperator() == no_not)
+        return getInverse(expr->queryChild(0)->queryBody());
+    return LINK(expr->queryBody());
+}
+
+bool areInverseExprs(IHqlExpression * left, IHqlExpression* right)
+{
+    if (left->getOperator() == no_not)
+        return left->queryChild(0)->queryBody() == right->queryBody();
+    if (right->getOperator() == no_not)
+        return right->queryChild(0)->queryBody() == left->queryBody();
+
+    node_operator leftOp = left->getOperator();
+    node_operator rightOp = right->getOperator();
+    if (leftOp != rightOp)
+    {
+        if (getInverseOp(leftOp) != rightOp)
+            return false;
+    }
+
+    OwnedHqlExpr inverseLeft = getInverse(left->queryBody());
+    return inverseLeft->queryBody() == right->queryBody();
+}
+
 
 IHqlExpression * getNegative(IHqlExpression * expr)
 {
@@ -7289,6 +7315,25 @@ IECLError * annotateExceptionWithLocation(IException * e, IHqlExpression * locat
     return createECLError(code, errorMsg.str(), location->querySourcePath()->str(), location->getStartLine(), location->getStartColumn(), 0);
 }
 
+StringBuffer & appendLocation(StringBuffer & s, IHqlExpression * location, const char * suffix)
+{
+    if (location)
+    {
+        int line = location->getStartLine();
+        int column = location->getStartColumn();
+        s.append(location->querySourcePath()->str());
+        if (line)
+        {
+            s.append("(").append(location->getStartLine());
+            if (column)
+                s.append(",").append(location->getStartColumn());
+            s.append(")");
+        }
+        s.append(suffix);
+    }
+    return s;
+}
+
 //---------------------------------------------------------------------------------------------------------------------
 
 static IHqlExpression * transformAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx)

+ 4 - 0
ecl/hql/hqlutil.hpp

@@ -113,8 +113,11 @@ extern HQL_API bool canSetBeAll(IHqlExpression * expr);
 extern HQL_API bool workflowContainsSchedule(IHqlExpression * colonExpr);
 extern HQL_API bool workflowContainsNonSchedule(IHqlExpression * colonExpr);
 
+extern HQL_API IHqlExpression * getNormalizedCondition(IHqlExpression * expr);
 extern HQL_API IHqlExpression * getInverse(IHqlExpression * op);
 extern HQL_API IHqlExpression * getNegative(IHqlExpression * value);
+extern HQL_API bool areInverseExprs(IHqlExpression * left, IHqlExpression* right);
+
 extern HQL_API IHqlExpression * convertRecordToTransform(IHqlExpression * record, bool canOmit);
 extern HQL_API void unwindFilterConditions(HqlExprArray & conds, IHqlExpression * expr);
 extern HQL_API unsigned getBestLengthEstimate(IHqlExpression * expr);
@@ -629,5 +632,6 @@ extern HQL_API IPropertyTree * createArchiveAttribute(IPropertyTree * module, co
 
 extern HQL_API IECLError * annotateExceptionWithLocation(IException * e, IHqlExpression * location);
 extern HQL_API IHqlExpression * convertAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx);
+extern HQL_API StringBuffer & appendLocation(StringBuffer & s, IHqlExpression * location, const char * suffix = NULL);
 
 #endif

+ 3 - 1
ecl/hqlcpp/CMakeLists.txt

@@ -28,7 +28,8 @@
 project( hqlcpp ) 
 
 set (    SRCS 
-         hqlcatom.cpp 
+         hqlalias.cpp
+         hqlcatom.cpp
          hqlccommon.cpp 
          hqlckey.cpp 
          hqlcpp.cpp 
@@ -59,6 +60,7 @@ set (    SRCS
          hqlttcpp.cpp 
          hqlwcpp.cpp 
     
+         hqlalias.hpp
          hqlcatom.hpp
          hqlccommon.hpp
          hqlcerrors.hpp

+ 366 - 0
ecl/hqlcpp/hqlalias.cpp

@@ -0,0 +1,366 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#include "platform.h"
+#include "jlib.hpp"
+#include "jset.hpp"
+
+#include "hql.hpp"
+#include "hqlutil.hpp"
+#include "hqltrans.ipp"
+
+#include "hqlalias.hpp"
+
+//---------------------------------------------------------------------------------------------------------------------
+
+static bool isSubsetOf(IHqlExpression * search, IHqlExpression * expr)
+{
+    loop
+    {
+        if (search->queryBody() == expr->queryBody())
+            return true;
+        if (expr->getOperator() != no_and)
+            return false;
+        //MORE: Remove if think it is worth doing more work
+//        return false;
+        //Check in order most likely to no recurse too deeply.
+        if (isSubsetOf(search, expr->queryChild(1)))
+            return true;
+        expr = expr->queryChild(0);
+    }
+}
+
+ConditionItem::ConditionItem(IHqlExpression * _expr, ConditionSet * _parent)
+: expr(_expr), parent(_parent)
+{
+}
+
+bool ConditionItem::isAlwaysConditionalOn(IHqlExpression * search) const
+{
+    if (isSubsetOf(search, expr))
+        return true;
+    if (!parent)
+        return false;
+    return parent->isAlwaysConditionalOn(search);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+
+bool ConditionSet::addOrCondition(IHqlExpression * expr, ConditionSet * parent)
+{
+    if (unconditional)
+        return false;
+
+    if (!expr)
+    {
+        setUnconditional();
+        return true;
+    }
+
+    ForEachItemIn(iMerge, conditions)
+    {
+        ConditionItem & cur = conditions.item(iMerge);
+        if (cur.equals(expr, parent))
+            return false;
+    }
+
+    conditions.append(*new ConditionItem(expr, parent));
+    return true;
+}
+
+void ConditionSet::setUnconditional()
+{
+    unconditional = true;
+    conditions.kill();
+}
+
+/*
+IHqlExpression * ConditionSet::getGuardCondition() const
+{
+    if (unconditional)
+        return NULL;
+    HqlExprArray values;
+    ForEachItemIn(i, conditions)
+        values.append(*conditions.item(i).getCondition());
+
+    OwnedITypeInfo boolType = makeBoolType();
+    return createBalanced(no_or, boolType, values);
+}
+*/
+
+bool ConditionSet::isAlwaysConditionalOn(IHqlExpression * expr)
+{
+    if (!expr)
+        return true;
+
+    if (unconditional)
+        return false;
+
+    //Cache the previous search value to avoid a potentially exponential search of the caller tree.
+    if (expr == isAlwaysCache.search)
+        return isAlwaysCache.value;
+
+    bool matches = true;
+    ForEachItemIn(i, conditions)
+    {
+        ConditionItem & cur = conditions.item(i);
+        if (!cur.isAlwaysConditionalOn(expr))
+        {
+            matches = false;
+            break;
+        }
+    }
+
+    isAlwaysCache.search.set(expr);
+    isAlwaysCache.value = matches;
+    return matches;
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------
+
+void ConditionTracker::pushCondition(IHqlExpression * expr, ConditionSet * parent)
+{
+    assertex(expr);
+    conditionStack.append(expr);
+    parentStack.append(parent);
+}
+
+void ConditionTracker::popCondition()
+{
+    conditionStack.pop();
+    parentStack.pop();
+}
+
+bool ConditionTracker::addActiveCondition(ConditionSet & conditions)
+{
+    if (conditionStack.ordinality() == 0)
+        return conditions.addOrCondition(NULL, NULL);
+    return conditions.addOrCondition(conditionStack.tos(), parentStack.tos());
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------
+
+class NestedIfInfo : public NewTransformInfo
+{
+public:
+    NestedIfInfo(IHqlExpression * _original) : NewTransformInfo(_original)
+    {
+        isShared = false;
+        containsIf = false;
+        conditions = NULL;
+    }
+    ~NestedIfInfo() { delete conditions; }
+
+    ConditionSet * queryConditions()
+    {
+        if (!conditions)
+            conditions = new ConditionSet;
+        return conditions;
+    }
+public:
+    ConditionSet * conditions;
+    bool isShared;
+    bool containsIf;
+};
+
+
+
+//MORE: Could remove dependancy on insideCompound if it was ok to have compound operators scattered through the
+//		contents of a compound item.  Probably would cause few problems, and would make life simpler
+class NestedIfTransformer : public NewHqlTransformer
+{
+public:
+    NestedIfTransformer();
+
+    IHqlExpression * process(IHqlExpression * expr);
+    bool process(const HqlExprArray & exprs, HqlExprArray & transformed);
+
+protected:
+    void analyseGatherIfs(IHqlExpression * expr);
+    void analyseNoteConditions(IHqlExpression * expr);
+    virtual void analyseExpr(IHqlExpression * expr);
+    virtual IHqlExpression * createTransformed(IHqlExpression * expr);
+    virtual ANewTransformInfo * createTransformInfo(IHqlExpression * expr)
+    {
+        return new NestedIfInfo(expr);
+    }
+
+    inline NestedIfInfo * queryBodyExtra(IHqlExpression * expr)	{ return static_cast<NestedIfInfo *>(queryTransformExtra(expr->queryBody())); }
+
+protected:
+    unsigned numIfs;
+    ConditionTracker tracker;
+};
+
+
+static HqlTransformerInfo nestedIfTransformerInfo("NestedIfTransformer");
+NestedIfTransformer::NestedIfTransformer() : NewHqlTransformer(nestedIfTransformerInfo)
+{
+    numIfs = 0;
+}
+
+void NestedIfTransformer::analyseExpr(IHqlExpression * expr)
+{
+    IHqlExpression * body = expr->queryBody();
+    switch (pass)
+    {
+    case 0:
+        analyseGatherIfs(body);
+        break;
+    case 1:
+        analyseNoteConditions(body);
+        break;
+    }
+}
+
+
+void NestedIfTransformer::analyseGatherIfs(IHqlExpression * expr)
+{
+    if (expr->getOperator() == no_if)
+        numIfs++;
+
+    NestedIfInfo * extra = queryBodyExtra(expr);
+    if (alreadyVisited(expr))
+    {
+        extra->isShared = true;
+        if (extra->containsIf)
+            numIfs++;
+        return;
+    }
+
+    unsigned prevIfCount = numIfs;
+    NewHqlTransformer::analyseExpr(expr);
+    if (prevIfCount != numIfs)
+        extra->containsIf = true;
+}
+
+
+void NestedIfTransformer::analyseNoteConditions(IHqlExpression * expr)
+{
+    node_operator op = expr->getOperator();
+    NestedIfInfo * extra = queryBodyExtra(expr);
+    if (extra->isShared || (op == no_if))
+    {
+        if (!tracker.addActiveCondition(*extra->queryConditions()))
+            return;
+    }
+
+    if (!extra->containsIf)
+        return;
+
+    if (op == no_if)
+    {
+        IHqlExpression * cond = expr->queryChild(0);
+        OwnedHqlExpr normalCond = getNormalizedCondition(cond);
+        analyseExpr(cond);
+        tracker.pushCondition(normalCond, extra->queryConditions());
+        analyseExpr(expr->queryChild(1));
+        tracker.popCondition();
+
+        IHqlExpression * falseExpr = queryRealChild(expr, 2);
+        if (falseExpr)
+        {
+            OwnedHqlExpr inverseCond = getInverse(normalCond);
+            tracker.pushCondition(inverseCond, extra->queryConditions());
+            analyseExpr(falseExpr);
+            tracker.popCondition();
+        }
+    }
+    else
+    {
+       NewHqlTransformer::analyseExpr(expr);
+    }
+}
+
+
+IHqlExpression * NestedIfTransformer::createTransformed(IHqlExpression * expr)
+{
+    if (expr->getOperator() == no_if)
+    {
+        IHqlExpression * cond = expr->queryChild(0);
+        IHqlExpression * falseExpr = queryRealChild(expr, 2);
+
+        OwnedHqlExpr normalCond = getNormalizedCondition(cond);
+        NestedIfInfo * extra = queryBodyExtra(expr);
+        IHqlExpression * selected = NULL;
+        if (extra->queryConditions()->isAlwaysConditionalOn(normalCond))
+        {
+            selected = expr->queryChild(1);
+        }
+        else if (falseExpr)
+        {
+            OwnedHqlExpr inverseCond = getInverse(normalCond);
+            if (extra->queryConditions()->isAlwaysConditionalOn(inverseCond))
+                selected = falseExpr;
+        }
+
+        if (selected)
+        {
+            const char * branch = (selected == falseExpr) ? "false" : "true";
+            StringBuffer exprText, locationText;
+            appendLocation(locationText, queryLocation(expr), ": ");
+            DBGLOG("%s%s replaced with %s branch since condition always %s", locationText.str(), queryChildNodeTraceText(exprText, expr), branch, branch);
+            OwnedHqlExpr ret = transform(selected);
+            return cloneMissingAnnotations(expr, ret);
+        }
+    }
+    return NewHqlTransformer::createTransformed(expr);
+}
+
+
+IHqlExpression * NestedIfTransformer::process(IHqlExpression * expr)
+{
+    analyse(expr, 0);
+    if (numIfs < 2)
+        return LINK(expr);
+    analyse(expr, 1);
+    return transformRoot(expr);
+}
+
+
+bool NestedIfTransformer::process(const HqlExprArray & exprs, HqlExprArray & transformed)
+{
+    ForEachItemIn(i1, exprs)
+        analyse(&exprs.item(i1), 0);
+    if (numIfs < 2)
+        return false;
+    ForEachItemIn(i2, exprs)
+        analyse(&exprs.item(i2), 1);
+    transformRoot(exprs, transformed);
+    return true;
+}
+
+
+IHqlExpression * optimizeNestedConditional(IHqlExpression * expr)
+{
+    NestedIfTransformer transformer;
+    return transformer.process(expr);
+}
+
+
+void optimizeNestedConditional(HqlExprArray & exprs)
+{
+    NestedIfTransformer transformer;
+    HqlExprArray transformed;
+    if (transformer.process(exprs, transformed))
+        exprs.swapWith(transformed);
+}

+ 83 - 0
ecl/hqlcpp/hqlalias.hpp

@@ -0,0 +1,83 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#ifndef __HQLALIAS_HPP_
+#define __HQLALIAS_HPP_
+
+#include "hqlexpr.hpp"
+
+class ConditionSet;
+class ConditionItem : public CInterface
+{
+public:
+    ConditionItem(IHqlExpression * _expr, ConditionSet * _parent);
+
+    bool equals(IHqlExpression * _expr, ConditionSet * _parent) const
+    {
+        return (expr == _expr) && (parent == _parent);
+    }
+
+    bool isAlwaysConditionalOn(IHqlExpression * expr) const;
+
+protected:
+    LinkedHqlExpr expr;
+    ConditionSet * parent;
+};
+
+
+class ConditionSet
+{
+public:
+    ConditionSet() { unconditional = false; nesting = 0; }
+
+    bool addOrCondition(IHqlExpression * expr, ConditionSet * parent);
+
+    bool isAlwaysConditionalOn(IHqlExpression * expr);
+    bool isUnconditonal() const { return unconditional; }
+    IHqlExpression * getGuardCondition() const;
+    void setUnconditional();
+
+protected:
+    CIArrayOf<ConditionItem> conditions;
+    unsigned nesting;
+    bool unconditional;
+    struct
+    {
+        HqlExprAttr search;
+        bool value;
+    } isAlwaysCache;
+};
+
+
+class ConditionTracker
+{
+public:
+    void pushCondition(IHqlExpression * expr, ConditionSet * parent);
+    void popCondition();
+    bool addActiveCondition(ConditionSet & conditions);
+
+protected:
+    PointerArrayOf<IHqlExpression> conditionStack;
+    PointerArrayOf<ConditionSet> parentStack;
+};
+
+
+IHqlExpression * optimizeNestedConditional(IHqlExpression * expr);
+void optimizeNestedConditional(HqlExprArray & exprs);
+
+#endif

+ 3 - 0
ecl/hqlcpp/hqlcpp.cpp

@@ -1668,6 +1668,9 @@ void HqlCppTranslator::cacheOptions()
         DebugOption(options.convertWhenExecutedToCompound,"convertWhenExecutedToCompound", queryLegacyEclSemantics()),
         DebugOption(options.standAloneExe,"standAloneExe", false),
         DebugOption(options.enableCompoundCsvRead,"enableCompoundCsvRead", true),
+        // The following works 99% of the time, but disabled due to potential problems with the ambiguity of LEFT
+        //possibly causing filters on nested records to be incorrectly removed.
+        DebugOption(options.optimizeNestedConditional,"optimizeNestedConditional", false),
     };
 
     //get options values from workunit

+ 1 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -699,6 +699,7 @@ struct HqlCppOptions
     bool                convertWhenExecutedToCompound;
     bool                standAloneExe;
     bool                enableCompoundCsvRead;
+    bool                optimizeNestedConditional;
 };
 
 //Any information gathered while processing the query should be moved into here, rather than cluttering up the translator class

+ 11 - 0
ecl/hqlcpp/hqlttcpp.cpp

@@ -38,6 +38,7 @@
 #include "hqlsource.ipp"
 #include "hqlvalid.hpp"
 #include "hqlerror.hpp"
+#include "hqlalias.hpp"
 
 #define TraceExprPrintLog(x, expr) TOSTRLOG(MCdebugInfo(300), unknownJob, x, (expr)->toString);
 //Following are for code that currently cause problems, but are probably a good idea
@@ -11886,6 +11887,16 @@ bool HqlCppTranslator::transformGraphForGeneration(IHqlExpression * query, Workf
         DEBUG_TIMER("EclServer: tree transform: hoist nested compound", msTick()-startTime);
     }
 
+    if (options.optimizeNestedConditional)
+    {
+        cycle_t time = msTick();
+        ForEachItemIn(idx, workflow)
+            optimizeNestedConditional(workflow.item(idx).queryExprs());
+        DEBUG_TIMER("EclServer: optimize nested conditional", msTick()-time);
+        traceExpressions("nested", workflow);
+        checkNormalized(workflow);
+    }
+
     checkNormalized(workflow);
     // Do this later so that counts of persistent results are optimized.
     if (options.optimizeThorCounts)

+ 35 - 0
ecl/regress/ifnest.ecl

@@ -0,0 +1,35 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#option ('targetClusterType', 'roxie');
+
+namesRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+integer2        age := 25;
+            END;
+
+namesTable := dataset('x',namesRecord,FLAT);
+
+x1 := true : stored('x1');
+x2 := true : stored('x2');
+
+y1 := if(x1, namesTable(age=1), namesTable(false));
+y2 := if(x1, y1, namesTable(false));
+output(y2);

+ 34 - 0
ecl/regress/ifnest2.ecl

@@ -0,0 +1,34 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#option ('targetClusterType', 'roxie');
+
+namesRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+integer2        age := 25;
+            END;
+
+namesTable := dataset('x',namesRecord,FLAT);
+
+x1 := true : stored('x1');
+
+y1 := namesTable(if(x1,age=1,age=5));
+y2 := if(x1, y1);
+output(y2);

+ 36 - 0
ecl/regress/ifnest3.ecl

@@ -0,0 +1,36 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#option ('targetClusterType', 'roxie');
+
+namesRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+integer2        age := 25;
+            END;
+
+namesTable := dataset('x',namesRecord,FLAT);
+
+x1 := true : stored('x1');
+
+ageValid := if(x1,namesTable.age=1,namesTable.age=5);
+
+y1 := namesTable(ageValid);
+y2 := if(not x1, y1);
+output(y2);

+ 35 - 0
ecl/regress/ifnest4.ecl

@@ -0,0 +1,35 @@
+/*##############################################################################
+
+    Copyright (C) 2011 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#option ('targetClusterType', 'roxie');
+
+namesRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+integer2        age := 25;
+            END;
+
+namesTable := dataset('x',namesRecord,FLAT);
+
+x1 := true : stored('x1');
+
+y1 := namesTable(if(x1,age=1,age=5));
+y2 := if(x1, y1);
+output(y2);
+output(y1);