瀏覽代碼

Work towards efficiently disambiguating left

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 13 年之前
父節點
當前提交
940957a4ec
共有 9 個文件被更改,包括 390 次插入7 次删除
  1. 6 0
      ecl/hql/hqlexpr.cpp
  2. 1 0
      ecl/hql/hqlexpr.hpp
  3. 26 1
      ecl/hql/hqlpmap.cpp
  4. 1 0
      ecl/hql/hqlpmap.hpp
  5. 8 4
      ecl/hql/hqltrans.cpp
  6. 8 0
      ecl/hql/hqlutil.cpp
  7. 1 0
      ecl/hql/hqlutil.hpp
  8. 292 2
      ecl/hqlcpp/hqlttcpp.cpp
  9. 47 0
      ecl/hqlcpp/hqlttcpp.ipp

+ 6 - 0
ecl/hql/hqlexpr.cpp

@@ -14862,6 +14862,12 @@ IHqlExpression * createUniqueSelectorSequence()
 }
 
 
+IHqlExpression * createSelectorSequence(unsigned __int64 seq)
+{
+    return createSequence(no_attr, NULL, _selectorSequence_Atom, seq);
+}
+
+
 static UniqueSequenceCounter rowsidSequence;
 IHqlExpression * createUniqueRowsId()
 {

+ 1 - 0
ecl/hql/hqlexpr.hpp

@@ -1492,6 +1492,7 @@ extern HQL_API IHqlExpression * createCounter();
 extern HQL_API IHqlExpression * createSelectorSequence();
 extern HQL_API IHqlExpression * createSequenceExpr();
 extern HQL_API IHqlExpression * createUniqueSelectorSequence();
+extern HQL_API IHqlExpression * createSelectorSequence(unsigned __int64 seq);
 extern HQL_API IHqlExpression * createDummySelectorSequence();
 extern HQL_API IHqlExpression * expandBetween(IHqlExpression * expr);
 extern HQL_API bool isAlwaysActiveRow(IHqlExpression * expr);

+ 26 - 1
ecl/hql/hqlpmap.cpp

@@ -115,6 +115,8 @@ static IHqlExpression * optimizedReplaceSelector(IHqlExpression * expr, IHqlExpr
 }
 
 
+//oldDataset is either an active selector (e.g., no_left) or a dataset.
+//newDataset is either an active selector (e.g., no_left) or a dataset (implying activerow(dataset) or newrow(dataset).
 IHqlExpression * replaceSelector(IHqlExpression * expr, IHqlExpression * oldDataset, IHqlExpression * newDataset)
 {
     if (!expr) return NULL;
@@ -125,7 +127,6 @@ IHqlExpression * replaceSelector(IHqlExpression * expr, IHqlExpression * oldData
     if (ret)
         return ret;
 
-
 #ifndef ENSURE_SELSEQ_UID
     node_operator op = oldDataset->getOperator();
     if (op == no_left || op == no_right)
@@ -159,6 +160,30 @@ void replaceSelectors(HqlExprArray & out, IHqlExpression * expr, unsigned first,
 
 //---------------------------------------------------------------------------------------------------------------------
 
+#if 0000
+void replaceSelectors(HqlExprArray & exprs, unsigned first, IHqlExpression * oldDataset, IHqlExpression * newDataset)
+{
+    unsigned max = exprs.ordinality();
+    unsigned iChild;
+    for (iChild = first; iChild < max; iChild++)
+    {
+        IHqlExpression *ret = optimizedReplaceSelector(&exprs.item(iChild), oldDataset->queryNormalizedSelector(), newDataset);
+        if (!ret)
+            break;
+        exprs.replace(*ret, iChild);
+    }
+
+    if (iChild == max)
+        return;
+
+    HqlMapSelectorTransformer transformer(oldDataset, newDataset);
+    for (; iChild < max; iChild++)
+        exprs.replace(*transformer.transformRoot(&exprs.item(iChild)), iChild);
+}
+
+
+#endif
+
 //NB: This can not be derived from NewHqlTransformer since it is called before the tree is normalised, and it creates
 //inconsistent expression trees.
 class HqlSelfRefTransformer

+ 1 - 0
ecl/hql/hqlpmap.hpp

@@ -172,6 +172,7 @@ extern HQL_API IHqlExpression * replaceSelector(IHqlExpression * expr, IHqlExpre
 extern HQL_API void replaceSelectors(HqlExprArray & out, IHqlExpression * expr, unsigned first, IHqlExpression * oldDataset, IHqlExpression * newDataset);
 extern HQL_API IHqlExpression * updateChildSelectors(IHqlExpression * expr, IHqlExpression * oldSelector, IHqlExpression * newSelector, unsigned firstChild);
 extern HQL_API IHqlExpression * updateMappedFields(IHqlExpression * expr, IHqlExpression * oldRecord, IHqlExpression * newSelector, unsigned firstChild);
+//////extern HQL_API void replaceSelectors(HqlExprArray & out, unsigned first, IHqlExpression * oldDataset, IHqlExpression * newDataset);
 extern HQL_API IHqlExpression * scopedReplaceSelector(IHqlExpression * expr, IHqlExpression * oldDataset, IHqlExpression * newDataset);
 extern HQL_API IHqlExpression * replaceSelfRefSelector(IHqlExpression * expr, IHqlExpression * newDataset);
 

+ 8 - 4
ecl/hql/hqltrans.cpp

@@ -1984,23 +1984,27 @@ IHqlExpression * replaceDataset(IHqlExpression * expr, IHqlExpression * original
 
 //---------------------------------------------------------------------------
 
-HqlMapSelectorTransformer::HqlMapSelectorTransformer(IHqlExpression * oldDataset, IHqlExpression * newDataset)
+HqlMapSelectorTransformer::HqlMapSelectorTransformer(IHqlExpression * oldDataset, IHqlExpression * newValue)
 {
     oldSelector.set(oldDataset);
-    IHqlExpression * newSelector = newDataset;
+    LinkedHqlExpr newSelector = newValue;
+    LinkedHqlExpr newDataset = newValue;
     if (newDataset->getOperator() == no_newrow)
-        newDataset = newDataset->queryChild(0);
+        newDataset.set(newDataset->queryChild(0));
+    else if (newDataset->isDataset())
+        newDataset.setown(ensureActiveRow(newDataset));
 
     node_operator op = oldDataset->getOperator();
 #ifndef ENSURE_SELSEQ_UID
     assertex(op != no_left && op != no_right);
 #endif
-    if (oldDataset->isDatarow() || op == no_activetable || op == no_self || op == no_selfref)
+    if (oldDataset->isDatarow() || (op == no_activetable) || (op == no_selfref))
     {
         setMappingOnly(oldDataset, newDataset);
     }
     else
     {
+        assertex(op != no_self);
         setMappingOnly(oldDataset, oldDataset);         // Don't change any new references to the dataset
     }
     setSelectorMapping(oldDataset, newSelector);

+ 8 - 0
ecl/hql/hqlutil.cpp

@@ -3449,6 +3449,14 @@ bool debugFindFirstDifference(IHqlExpression * left, IHqlExpression * right)
     return foundDifference();//something else
 }
 
+void debugTrackDifference(IHqlExpression * expr)
+{
+    static IHqlExpression * prev;
+    if (prev && prev != expr)
+        debugFindFirstDifference(prev, expr);
+    prev = expr;
+}
+
 //------------------------------------------------------------------------------------------------
 
 static IHqlExpression * expandConditions(IHqlExpression * expr, DependenciesUsed & dependencies, HqlExprArray & conds, HqlExprArray & values)

+ 1 - 0
ecl/hql/hqlutil.hpp

@@ -606,6 +606,7 @@ extern HQL_API void encryptEclAttribute(IStringVal & out, size32_t len, const vo
 extern void decryptEclAttribute(MemoryBuffer & out, const char * in);
 
 extern HQL_API bool debugFindFirstDifference(IHqlExpression * left, IHqlExpression * right);
+extern HQL_API void debugTrackDifference(IHqlExpression * expr);
 
 extern HQL_API StringBuffer & convertToValidLabel(StringBuffer &out, const char * in, unsigned inlen);
 extern HQL_API bool arraysSame(CIArray & left, CIArray & right);

+ 292 - 2
ecl/hqlcpp/hqlttcpp.cpp

@@ -9320,6 +9320,275 @@ void ImplicitAliasTransformer::process(HqlExprArray & exprs)
 }
 //---------------------------------------------------------------------------------------------------------------------
 
+SharedTableInfo * LeftRightTransformInfo::uses(IHqlExpression * tableBody) const
+{
+   ForEachItemIn(i, sharedTables)
+   {
+        SharedTableInfo & cur = sharedTables.item(i);
+        if (cur.dataset == tableBody)
+            return &cur;
+   }
+   return NULL;
+}
+
+void LeftRightTransformInfo::add(SharedTableInfo * table)
+{
+    sharedTables.append(*LINK(table));
+}
+
+bool LeftRightTransformInfo::noteUsed(IHqlExpression * seq)
+{
+    if (!seqs.contains(*seq))
+        seqs.append(*seq);
+
+    if (seqs.ordinality() > 1)
+    {
+        shared.setown(new SharedTableInfo(original, 0));
+        return true;
+    }
+    return false;
+}
+
+void LeftRightTransformInfo::addAmbiguity(SharedTableInfo * table)
+{
+    containsAmbiguity = true;
+    merge(table);
+}
+
+void LeftRightTransformInfo::merge(SharedTableInfo * table)
+{
+    ForEachItemIn(i, sharedTables)
+    {
+        SharedTableInfo & cur = sharedTables.item(i);
+        if (cur.dataset == table->dataset)
+        {
+            if (cur.depth < table->depth)
+                sharedTables.replace(*LINK(table), i);
+            return;
+        }
+    }
+    add(table);
+}
+
+void LeftRightTransformInfo::inherit(const LeftRightTransformInfo * other)
+{
+    ForEachItemIn(i, other->sharedTables)
+        merge(&other->sharedTables.item(i));
+}
+
+
+/*
+This transformer is responsible for reducing the number of selseq used as sequence numbers - so that they only remain
+when nesting makes the selectors ambiguous.  That allows expressions to be commoned up that wouldn't otherwise.
+*/
+static HqlTransformerInfo LeftRightTransformerInfo("LeftRightTransformer");
+LeftRightTransformer::LeftRightTransformer() : NewHqlTransformer(LeftRightTransformerInfo)
+{
+    seenShared = true;
+}
+
+
+void LeftRightTransformer::analyseExpr(IHqlExpression * _expr)
+{
+    IHqlExpression * body = _expr->queryBody();
+    if (alreadyVisited(body))
+        return;
+
+    NewHqlTransformer::analyseExpr(body);
+    if (pass == 0)
+    {
+        IHqlExpression * left = NULL;
+        IHqlExpression * right = NULL;
+        switch (getChildDatasetType(body))
+        {
+        case childdataset_left:
+        case childdataset_datasetleft:
+            left = body->queryChild(0);
+            break;
+        case childdataset_same_left_right:
+        case childdataset_nway_left_right:
+        case childdataset_top_left_right:
+            left = body->queryChild(0);
+            right = body->queryChild(0);
+            break;
+        case childdataset_leftright:
+            left = body->queryChild(0);
+            right = body->queryChild(1);
+            break;
+        }
+
+        if (left)
+        {
+            LeftRightTransformInfo * extra = queryExtra(body);
+            IHqlExpression * selSeq = querySelSeq(body);
+            OwnedHqlExpr seq = createDummySelectorSequence();
+            extra->rawLeft.setown(createSelector(no_left, left, seq));
+            incUsage(extra->rawLeft, selSeq);
+            if (right)
+            {
+                //no_left is used deliberately in the following to avoid complications where right matches
+                //but left doesn't, causing the depths to be messed up.
+                extra->rawRight.setown(createSelector(no_left, right, seq));
+                if (extra->rawLeft != extra->rawRight)
+                    incUsage(extra->rawRight, selSeq);
+                else
+                    extra->rawRight.clear();
+            }
+        }
+    }
+    else
+    {
+        LeftRightTransformInfo * extra = queryExtra(body);
+        if (extra->rawLeft)
+        {
+            LeftRightTransformInfo * leftExtra = queryExtra(extra->rawLeft);
+            if (leftExtra->shared)
+                extra->add(leftExtra->shared);
+        }
+
+        switch (body->getOperator())
+        {
+        case no_activerow:
+        case no_filepos:
+        case no_file_logicalname:
+        case no_offsetof:
+        case no_joined:
+        case no_colon:
+        case no_globalscope:
+        case no_attr:
+            return;
+        case no_select:
+            {
+                bool isNew;
+                IHqlExpression * ds = querySelectorDataset(body, isNew);
+                if (isNew)
+                {
+                    LeftRightTransformInfo * dsExtra = queryExtra(ds->queryBody());
+                    extra->inherit(dsExtra);
+                }
+                return;
+            }
+        }
+
+        IHqlExpression * left = extra->rawLeft;
+        IHqlExpression * right = extra->rawRight;
+        ForEachChild(i, body)
+        {
+            IHqlExpression * cur = body->queryChild(i);
+            LeftRightTransformInfo * childExtra = queryExtra(cur->queryBody());
+            //If this is one of the arguments to an operation which has an active top dataset,
+            //check to see if any of the contained expressions reference this item
+            if ((i != 0) && (left || right))
+            {
+                if (left)
+                {
+                    SharedTableInfo * matchLeft = childExtra->uses(left);
+                    SharedTableInfo * matchRight = right ? childExtra->uses(right) : NULL;
+                    if (matchLeft || matchRight)
+                    {
+                        unsigned leftDepth = matchLeft ? matchLeft->depth : 0;
+                        unsigned rightDepth = matchRight ? matchRight->depth : 0;
+                        unsigned depth = leftDepth > rightDepth ? leftDepth : rightDepth;
+                        SharedTableInfo * nested = createAmbiguityInfo(left, depth+1);
+                        extra->addAmbiguity(nested);
+                        if (matchRight)
+                        {
+                            SharedTableInfo * nested = createAmbiguityInfo(right, depth+1);
+                            extra->addAmbiguity(nested);
+                        }
+                    }
+                }
+            }
+            extra->inherit(childExtra);
+        }
+    }
+}
+
+void LeftRightTransformer::incUsage(IHqlExpression * expr, IHqlExpression * seq)
+{
+    //MORE: Needs to keep track of the sequences that were used with it, so know if needs disambiguating.
+    LeftRightTransformInfo * extra = queryExtra(expr);
+    if (extra->noteUsed(seq))
+        seenShared = true;
+}
+
+SharedTableInfo * LeftRightTransformer::createAmbiguityInfo(IHqlExpression * dataset, unsigned depth)
+{
+    ForEachItemIn(i, ambiguousTables)
+    {
+        SharedTableInfo & cur = ambiguousTables.item(i);
+        if ((cur.dataset == dataset) && (depth == cur.depth))
+            return &cur;
+    }
+    ambiguousTables.append(*new SharedTableInfo(dataset, depth));
+    return &ambiguousTables.tos();
+}
+
+
+ANewTransformInfo * LeftRightTransformer::createTransformInfo(IHqlExpression * expr)
+{
+    return CREATE_NEWTRANSFORMINFO(LeftRightTransformInfo, expr);
+}
+
+IHqlExpression * LeftRightTransformer::createTransformed(IHqlExpression * expr)
+{
+    IHqlExpression * body = expr->queryBody();
+    if (expr != body)
+    {
+        OwnedHqlExpr newBody = transform(body);
+        return expr->cloneAllAnnotations(newBody);
+    }
+
+    OwnedHqlExpr transformed = NewHqlTransformer::createTransformed(expr);
+    updateOrphanedSelectors(transformed, expr);
+
+    LeftRightTransformInfo * extra = queryExtra(body);
+    SharedTableInfo * matchLeft = extra->rawLeft ? extra->uses(extra->rawLeft) : NULL;
+    if (matchLeft)
+    {
+        childDatasetType dsType = getChildDatasetType(expr);
+        IHqlExpression * left = transformed->queryChild(0);
+        IHqlExpression * right = hasRight(dsType) ? (hasSameLeftRight(dsType) ? left : transformed->queryChild(1)) : NULL;
+
+        IHqlExpression * oldSelSeq = querySelSeq(expr);
+        OwnedHqlExpr newSelSeq = createSelectorSequence(matchLeft->depth);
+        OwnedHqlExpr oldLeft = createSelector(no_left, left, oldSelSeq);
+        OwnedHqlExpr newLeft = createSelector(no_left, left, newSelSeq);
+
+        //Replace dataset with an aliased variety, and remap all the selectors
+        HqlExprArray mapped;
+        replaceSelectors(mapped, transformed, 1, oldLeft, newLeft);
+
+        if (right)
+        {
+            OwnedHqlExpr oldRight = createSelector(no_right, right, oldSelSeq);
+            OwnedHqlExpr newRight = createSelector(no_right, right, newSelSeq);
+            unsigned firstArg = (hasSameLeftRight(dsType) ? 0 : 1);
+            replaceSelectors(mapped, firstArg, oldRight, newRight);
+        }
+
+        HqlExprArray args;
+        args.append(*LINK(left));
+        appendArray(args, mapped);
+        args.zap(*oldSelSeq);
+        args.append(*LINK(newSelSeq));
+        transformed.setown(transformed->clone(args));
+    }
+    return transformed.getClear();
+}
+
+void LeftRightTransformer::process(HqlExprArray & exprs)
+{
+    analyseArray(exprs, 0);
+    if (!seenShared)
+        return;
+    analyseArray(exprs, 1);
+    HqlExprArray transformed;
+    transformRoot(exprs, transformed);
+    replaceArray(exprs, transformed);
+}
+//---------------------------------------------------------------------------------------------------------------------
+
 /*
   Common up expressions so that all references to the same expression have identical symbols, annotations.
   Generally it improves the code a lot - especially when macros are used.  However there are occasional problems....
@@ -11528,7 +11797,14 @@ IHqlExpression * HqlTreeNormalizer::createTransformedBody(IHqlExpression * expr)
                 //Not sure the following is really necessary, but will reduce in memory tree size....
                 //also saves complications from having weird attributes in the tree
                 if (expr->numChildren() > 0)
+                {
+                    //Make sure we ignore any line number information on the parameters mangled with the uid - otherwise
+                    //they may create too many unique ids.
+                    IHqlExpression * normalForm = queryLocationIndependent(expr);
+                    if (normalForm != expr)
+                        return transform(normalForm);
                     return createSelectorSequence();
+                }
             }
 #endif
             break;
@@ -11755,21 +12031,22 @@ void normalizeHqlTree(HqlCppTranslator & translator, HqlExprArray & exprs)
     }
 
 #ifdef USE_SELSEQ_UID
+#ifndef ENSURE_SELSEQ_UID
     if (translator.queryOptions().detectAmbiguousSelector || translator.queryOptions().allowAmbiguousSelector)
     {
         LeftRightSelectorNormalizer transformer(translator.queryOptions().allowAmbiguousSelector);
 
         transformer.analyseArray(exprs, 0);
-#ifndef ENSURE_SELSEQ_UID
+
         if (!transformer.containsAmbiguity())
         {
             HqlExprArray transformed;
             transformer.transformRoot(exprs, transformed);
             replaceArray(exprs, transformed);
         }
-#endif
     }
 #endif
+#endif
 
     if (false)
     {
@@ -12036,6 +12313,19 @@ bool HqlCppTranslator::transformGraphForGeneration(IHqlExpression * query, Workf
     checkNormalized(workflow);
     DEBUG_TIMER("EclServer: tree transform: stored results", msTick()-time4);
 
+#ifdef USE_SELSEQ_UID
+    {
+        unsigned time = msTick();
+        ForEachItemIn(i, workflow)
+        {
+            LeftRightTransformer normalizer;
+            normalizer.process(workflow.item(i).queryExprs());
+        }
+        DEBUG_TIMERX(queryTimeReporter(), "EclServer: tree transform: left right", msTick()-time);
+        //traceExpressions("after implicit alias", workflow);
+    }
+#endif
+
     if (queryOptions().createImplicitAliases)
     {
         unsigned time = msTick();

+ 47 - 0
ecl/hqlcpp/hqlttcpp.ipp

@@ -854,6 +854,8 @@ public:
 public:
     CIArrayOf<SharedTableInfo> sharedTables;
     Owned<SharedTableInfo> shared;
+    OwnedHqlExpr        rawLeft;
+    OwnedHqlExpr        rawRight;
     bool                containsAmbiguity;
 };
 
@@ -883,6 +885,51 @@ protected:
 
 //---------------------------------------------------------------------------
 
+class LeftRightTransformInfo : public NewTransformInfo
+{
+public:
+    LeftRightTransformInfo(IHqlExpression * _expr) : NewTransformInfo(_expr) { containsAmbiguity = false; }
+
+    void add(SharedTableInfo * table);
+    void addAmbiguity(SharedTableInfo * table);
+    void inherit(const LeftRightTransformInfo * other);
+    void merge(SharedTableInfo * table);
+    bool noteUsed(IHqlExpression * seq);
+    SharedTableInfo * uses(IHqlExpression * tableBody) const;
+
+public:
+    CIArrayOf<SharedTableInfo> sharedTables;
+    Owned<SharedTableInfo> shared;
+    HqlExprCopyArray    seqs;
+    OwnedHqlExpr        rawLeft;
+    OwnedHqlExpr        rawRight;
+    bool                containsAmbiguity;
+};
+
+
+class LeftRightTransformer : public NewHqlTransformer
+{
+public:
+    LeftRightTransformer();
+
+    void process(HqlExprArray & exprs);
+
+protected:
+    virtual void analyseExpr(IHqlExpression * expr);
+    virtual ANewTransformInfo * createTransformInfo(IHqlExpression * expr);
+    virtual IHqlExpression * createTransformed(IHqlExpression * expr);
+
+    inline LeftRightTransformInfo * queryExtra(IHqlExpression * expr)  { return (LeftRightTransformInfo *)queryTransformExtra(expr); }
+    SharedTableInfo * createAmbiguityInfo(IHqlExpression * dataset, unsigned depth);
+    void incUsage(IHqlExpression * expr, IHqlExpression * seq);
+
+protected:
+    CIArrayOf<SharedTableInfo> ambiguousTables;
+    bool seenShared;
+};
+
+//---------------------------------------------------------------------------
+
 class ForceLocalTransformInfo : public NewTransformInfo
 {
     friend class ForceLocalTransformer;