Forráskód Böngészése

HPCC-20177 Optimize CASE() returning a row

This also fixes a problem with the code for default values being duplicated

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 6 éve
szülő
commit
c9430e9357

+ 62 - 0
ecl/hql/hqlutil.cpp

@@ -4608,6 +4608,68 @@ IHqlExpression * combineIfsToMap(IHqlExpression * expr)
     return createWrapper(no_map, expr->queryType(), args);
 }
 
+class CaseMatcher
+{
+public:
+    void matchIfs(IHqlExpression * expr)
+    {
+        if (expr->getOperator() == no_if)
+        {
+            IHqlExpression * test = expr->queryChild(0);
+            if (test->getOperator() == no_eq)
+            {
+                IHqlExpression * search = test->queryChild(0);
+                if (!cond || (search == cond))
+                {
+                    cond = search;
+                    keys.append(*LINK(test->queryChild(1)));
+                    values.append(*LINK(expr->queryChild(1)));
+                    IHqlExpression * elseExpr = expr->queryChild(2);
+                    if (elseExpr)
+                        matchIfs(elseExpr);
+                    else
+                        defaultExpr.setown(createNullExpr(expr));
+                    return;
+                }
+            }
+        }
+        defaultExpr.set(expr);
+    }
+
+    IHqlExpression * createCase(ITypeInfo * type)
+    {
+        HqlExprArray args;
+        args.append(*LINK(cond));
+        ForEachItemIn(i, keys)
+        {
+            IHqlExpression & curValue = values.item(i);
+            args.append(*createValue(no_mapto, curValue.getType(), LINK(&keys.item(i)), LINK(&curValue)));
+        }
+        args.append(*LINK(defaultExpr));
+        return createWrapper(no_case, type, args);
+    }
+
+    bool worthMapping()
+    {
+        return keys.ordinality() > 1;
+    }
+
+protected:
+    HqlExprArray keys;
+    HqlExprArray values;
+    IHqlExpression * cond = nullptr;
+    OwnedHqlExpr defaultExpr;
+};
+
+IHqlExpression * combineIfsToCase(IHqlExpression * expr)
+{
+    CaseMatcher matcher;
+    matcher.matchIfs(expr);
+    if (matcher.worthMapping())
+        return matcher.createCase(expr->queryType());
+    return nullptr;
+}
+
 bool castPreservesValueAndOrder(IHqlExpression * expr)
 {
     assertex(isCast(expr));

+ 1 - 0
ecl/hql/hqlutil.hpp

@@ -194,6 +194,7 @@ extern HQL_API IHqlExpression * getFailMessage(IHqlExpression * failExpr, bool n
 extern HQL_API IAtom * queryCsvTableEncoding(IHqlExpression * tableExpr);
 extern HQL_API IAtom * queryCsvEncoding(IHqlExpression * csvAttr);
 extern HQL_API IHqlExpression * combineIfsToMap(IHqlExpression * expr);
+extern HQL_API IHqlExpression * combineIfsToCase(IHqlExpression * expr);
 extern HQL_API IHqlExpression * appendLocalAttribute(IHqlExpression * expr);
 extern HQL_API IHqlExpression * removeLocalAttribute(IHqlExpression * expr);
 extern HQL_API IHqlExpression * removeAttribute(IHqlExpression * expr, IAtom * attr);

+ 12 - 0
ecl/hqlcpp/hqlcpp.cpp

@@ -3766,6 +3766,14 @@ void HqlCppTranslator::buildStmt(BuildCtx & _ctx, IHqlExpression * expr)
     case no_if:
         doBuildStmtIf(ctx, expr);
         return;
+    case no_case:
+    {
+        HqlCppCaseInfo info(*this);
+        doBuildCaseInfo(expr, info);
+        ExprEvaluateProcessor assigner;
+        info.buildProcess(ctx, assigner);
+        return;
+    }
     case no_call:
     case no_externalcall:
         doBuildStmtCall(ctx, expr);
@@ -7581,6 +7589,10 @@ void HqlCppTranslator::doBuildExprIf(BuildCtx & ctx, IHqlExpression * expr, CHql
 
 void HqlCppTranslator::doBuildStmtIf(BuildCtx & ctx, IHqlExpression * expr)
 {
+    OwnedHqlExpr converted = combineIfsToCase(expr);
+    if (converted)
+        return buildStmt(ctx, converted);
+
     BuildCtx subctx(ctx);
     CHqlBoundExpr cond;
     buildCachedExpr(subctx, expr->queryChild(0), cond);

+ 1 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -1336,6 +1336,7 @@ public:
     void doBuildRowAssignCreateRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr);
     void doBuildRowAssignSerializeRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr);
 
+    IReferenceSelector * doBuildRowCase(BuildCtx & ctx, IHqlExpression * expr);
     IReferenceSelector * doBuildRowDeserializeRow(BuildCtx & ctx, IHqlExpression * expr);
     IReferenceSelector * doBuildRowFromXMLorJSON(BuildCtx & ctx, IHqlExpression * expr);
     IReferenceSelector * doBuildRowIdToBlob(BuildCtx & ctx, IHqlExpression * expr, bool isNew);

+ 1 - 0
ecl/hqlcpp/hqlcppcase.cpp

@@ -1034,6 +1034,7 @@ IHqlExpression * HqlCppCaseInfo::createResultsExpr(IHqlExpression * matchVar, bo
     HqlExprArray choosePairs;
     cvtChooseListToPairs(choosePairs, newlist, 0);
 
+    *includedDefault = true;
     IHqlExpression * caseExpr = createOpenValue(no_case, LINK(retType));
     caseExpr->addOperand(LINK(matchVar));
     ForEachItemIn(idx2, choosePairs)

+ 50 - 0
ecl/hqlcpp/hqlcppds.cpp

@@ -93,6 +93,10 @@ IReferenceSelector * HqlCppTranslator::doBuildRowIf(BuildCtx & ctx, IHqlExpressi
         return buildNewRow(ctx, expr->queryChild(branch));
     }
 
+    OwnedHqlExpr converted = combineIfsToCase(expr);
+    if (converted)
+        return buildNewRow(ctx, converted);
+
     IHqlExpression * trueBranch = expr->queryChild(1);
     IHqlExpression * falseBranch = expr->queryChild(2);
 
@@ -121,6 +125,41 @@ IReferenceSelector * HqlCppTranslator::doBuildRowIf(BuildCtx & ctx, IHqlExpressi
 }
 
 
+class ExprBranchAssignProcessor : implements IExprProcessor
+{
+public:
+    ExprBranchAssignProcessor(BoundRow * _targetRow, IHqlExpression * _rowExpr) : targetRow(_targetRow), rowExpr(_rowExpr) {}
+
+    virtual void process(HqlCppTranslator & translator, BuildCtx & ctx, IHqlExpression * expr) override
+    {
+        ctx.associateExpr(queryConditionalRowMarker(), rowExpr);
+        translator.doBuildRowIfBranch(ctx, ctx, targetRow, expr);
+    }
+
+private:
+    BoundRow * targetRow;
+    IHqlExpression * rowExpr;
+};
+
+
+IReferenceSelector * HqlCppTranslator::doBuildRowCase(BuildCtx & ctx, IHqlExpression * expr)
+{
+    HqlCppCaseInfo info(*this);
+    doBuildCaseInfo(expr, info);
+
+    //Ideally should have a constant modifier on the following row...
+    Owned<ITypeInfo> rowType = makeReferenceModifier(expr->getType());
+    OwnedHqlExpr rowExpr = ctx.getTempDeclare(rowType, NULL);
+    Owned<BoundRow> row = createBoundRow(expr->queryBody(), rowExpr);
+
+    ExprBranchAssignProcessor assigner(row, rowExpr);
+    info.buildProcess(ctx, assigner);
+
+    ctx.associate(*row);
+    return createReferenceSelector(row);
+}
+
+
 IReferenceSelector * HqlCppTranslator::doBuildRowDeserializeRow(BuildCtx & ctx, IHqlExpression * expr)
 {
     IHqlExpression * srcRow = expr->queryChild(0);
@@ -431,6 +470,8 @@ IReferenceSelector * HqlCppTranslator::buildNewRow(BuildCtx & ctx, IHqlExpressio
         return buildActiveRow(ctx, expr->queryChild(0));
     case no_if:
         return doBuildRowIf(ctx, expr);
+    case no_case:
+        return doBuildRowCase(ctx, expr);
     case no_id2blob:
         return doBuildRowIdToBlob(ctx, expr, true);
     case no_index:
@@ -4675,6 +4716,15 @@ void HqlCppTranslator::buildRowAssign(BuildCtx & ctx, IReferenceSelector * targe
             }
         }
         break;
+    case no_case:
+    case no_map:
+        {
+            HqlCppCaseInfo info(*this);
+            doBuildCaseInfo(expr, info);
+            ExprRowAssignProcessor assigner(target);
+            info.buildProcess(ctx, assigner);
+            return;
+        }
         /*
     case no_externalcall:
         //MORE: Should assign directly to the target, but may not be very easy....

+ 53 - 0
ecl/regress/issue20177.ecl

@@ -0,0 +1,53 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+            END;
+
+nvRecord := { string name, string value };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+mkRecord(string x, string y) := ROW(mkpair(x, y));
+
+mknv(unsigned c, inputRecord l) :=
+    CASE(c,
+     1=>mkRecord('surname', l.surname),
+     2=>mkRecord('forename', l.forename),
+     3=>mkRecord('age', l.age),
+     4=>mkRecord('extra1', l.extra1),
+     5=>mkRecord('extra2', l.extra2),
+     6=>mkRecord('extra3', l.extra3),
+     7=>mkRecord('extra4', l.extra4),
+     mkRecord('','')
+     );
+
+n := NORMALIZE(names, 7, TRANSFORM(nvRecord, SELF := mknv(COUNTER, LEFT)));
+output(n);

+ 53 - 0
ecl/regress/issue20177a.ecl

@@ -0,0 +1,53 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+            END;
+
+nvRecord := { string name, string value };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+mkRecord(string x, string y) := ROW(mkpair(x, y));
+
+mknv(unsigned c, inputRecord l) :=
+    CASE(c,
+     1=>mkRecord('surname', l.surname),
+     2=>mkRecord('forename', l.forename),
+     3=>mkRecord('age', l.age),
+     5=>mkRecord('extra1', l.extra1),
+     6=>mkRecord('extra2', l.extra2),
+     7=>mkRecord('extra3', l.extra3),
+     8=>mkRecord('extra4', l.extra4),
+     mkRecord('','')
+     );
+
+n := NORMALIZE(names, 8, TRANSFORM(nvRecord, SELF := mknv(COUNTER, LEFT)));
+output(n);

+ 55 - 0
ecl/regress/issue20177b.ecl

@@ -0,0 +1,55 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+            END;
+
+nvRecord := { string name, string value };
+nvRecords := { DATASET(nvRecord) values };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+mkRows(string x, string y) := DATASET([mkpair(x,y), mkpair(x,y+1)]);
+
+mknv(unsigned c, inputRecord l) :=
+    CASE(c,
+     1=>mkRows('surname', l.surname),
+     2=>mkRows('forename', l.forename),
+     3=>mkRows('age', l.age),
+     4=>mkRows('extra1', l.extra1),
+     5=>mkRows('extra2', l.extra2),
+     6=>mkRows('extra3', l.extra3),
+     7=>mkRows('extra4', l.extra4),
+     mkRows('','')
+     );
+
+n := NORMALIZE(names, 7, TRANSFORM(nvRecords, SELF.values := mknv(COUNTER, LEFT)));
+output(n);

+ 55 - 0
ecl/regress/issue20177c.ecl

@@ -0,0 +1,55 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+            END;
+
+nvRecord := { string name, string value };
+nvRecords := { DATASET(nvRecord) values };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+mkRows(string x, string y) := DATASET([mkpair(x,y), mkpair(x,y+1)]);
+
+mknv(unsigned c, inputRecord l) :=
+    CASE(c,
+     1=>mkRows('surname', l.surname),
+     2=>mkRows('forename', l.forename),
+     3=>mkRows('age', l.age),
+     5=>mkRows('extra1', l.extra1),
+     6=>mkRows('extra2', l.extra2),
+     7=>mkRows('extra3', l.extra3),
+     8=>mkRows('extra4', l.extra4),
+     mkRows('','')
+     );
+
+n := NORMALIZE(names, 8, TRANSFORM(nvRecords, SELF.values := mknv(COUNTER, LEFT)));
+output(n);

+ 56 - 0
ecl/regress/issue20177e.ecl

@@ -0,0 +1,56 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+unsigned        cnt := 0;
+            END;
+
+nvRecord := { string name, string value };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+n := NORMALIZE(names, 7, TRANSFORM(inputRecord, SELF.cnt := COUNTER; SELF := LEFT));
+
+
+outRecord(string x, string y) := OUTPUT(DATASET([{x,y}],nvRecord),NAMED('pairs'),extend);
+
+outnv(unsigned c, inputRecord l) :=
+    CASE(c,
+     1=>outRecord('surname', l.surname),
+     2=>outRecord('forename', l.forename),
+     3=>outRecord('age', l.age),
+     4=>outRecord('extra1', l.extra1),
+     5=>outRecord('extra2', l.extra2),
+     6=>outRecord('extra3', l.extra3),
+     7=>outRecord('extra4', l.extra4),
+     outRecord('','')
+     );
+
+apply(names, outnv(names.cnt, row(names)));

+ 55 - 0
ecl/regress/issue20177f.ecl

@@ -0,0 +1,55 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 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.
+############################################################################## */
+
+inputRecord :=
+            RECORD
+string20        surname;
+string10        forename;
+string3         age;
+string          extra1;
+string          extra2;
+string          extra3;
+string          extra4;
+            END;
+
+nvRecord := { string name, string value };
+nvRecords := { DATASET(nvRecord) values };
+
+names := dataset([
+    { 'Gavin', 'Smith', '34', 'a', 'b', 'c', 'd' },
+    { 'John', 'Doe', '32', 'x', 'a', 'abc', 'q' }
+    ], inputRecord);
+
+
+mkpair(string x, string y) := TRANSFORM(nvRecord, SELF.name := x; SELF.value := y);
+
+mkRows(string x, string y) := DATASET([mkpair(x,y), mkpair(x,y+1)]);
+
+mknv(string3 c, inputRecord l) :=
+    CASE(c,
+     '1'=>mkRows('surname', l.surname),
+     '2'=>mkRows('forename', l.forename),
+     '3'=>mkRows('age', l.age),
+     '5'=>mkRows('extra1', l.extra1),
+     '6'=>mkRows('extra2', l.extra2),
+     '7'=>mkRows('extra3', l.extra3),
+     '8'=>mkRows('extra4', l.extra4),
+     mkRows('','')
+     );
+
+n := NORMALIZE(names, 8, TRANSFORM(nvRecords, SELF.values := mknv(NOFOLD((string1)COUNTER), LEFT)));
+output(n);