diff --git a/ecl/hqlcpp/hqlckey.cpp b/ecl/hqlcpp/hqlckey.cpp
index cf506b2cc8b..67a31d4c748 100644
--- a/ecl/hqlcpp/hqlckey.cpp
+++ b/ecl/hqlcpp/hqlckey.cpp
@@ -307,14 +307,38 @@ IHqlExpression * KeyedJoinInfo::querySimplifiedKey(IHqlExpression * expr)
IHqlExpression * queryBaseIndexForKeyedJoin(IHqlExpression * expr)
{
- if (expr->getOperator() == no_if)
+ node_operator op = expr->getOperator();
+ if (op == no_if)
{
IHqlExpression * left = queryBaseIndexForKeyedJoin(expr->queryChild(1));
IHqlExpression * right = queryBaseIndexForKeyedJoin(expr->queryChild(2));
if (left && right)
- return left;
+ {
+ //IF (cond, index) and IF(cond, null, index) should be allowed, and will return the index
+ if (left->getOperator() != no_null)
+ return left;
+ return right;
+ }
return nullptr;
}
+ else if (op == no_chooseds)
+ {
+ IHqlExpression * result = nullptr;
+ ForEachChildFrom(i, expr, 1)
+ {
+ IHqlExpression * match = queryBaseIndexForKeyedJoin(expr->queryChild(i));
+ if (!match)
+ return nullptr;
+ if (!result || result->getOperator() == no_null)
+ result = match;
+ }
+ return result;
+ }
+ else if (op == no_null)
+ return expr;
+ else if (op == no_split)
+ return queryBaseIndexForKeyedJoin(expr->queryChild(0));
+
return queryPhysicalRootTable(expr);
}
diff --git a/roxie/ccd/ccdserver.cpp b/roxie/ccd/ccdserver.cpp
index eb3b643cff3..91e9d6223a9 100644
--- a/roxie/ccd/ccdserver.cpp
+++ b/roxie/ccd/ccdserver.cpp
@@ -9570,6 +9570,11 @@ class CRoxieServerThroughSpillActivity : public CRoxieServerActivity
CRoxieServerActivity::stop();
};
+ virtual IIndexReadActivityInfo *queryIndexReadActivity() override
+ {
+ return input->queryIndexReadActivity();
+ }
+
void reset(unsigned oid)
{
if (state != STATEreset) // make sure input is only reset once
@@ -21076,6 +21081,14 @@ class CRoxieServerCaseActivity : public CRoxieServerMultiInputBaseActivity
CRoxieServerMultiInputBaseActivity::reset();
}
+ virtual IIndexReadActivityInfo *queryIndexReadActivity()
+ {
+ //CHOOSE defaults to the last argument if out of range.
+ if (cond >= numInputs)
+ cond = numInputs - 1;
+ return inputArray[cond]->queryIndexReadActivity();
+ }
+
virtual const void *nextRow()
{
ActivityTimer t(activityStats, timeActivities);
diff --git a/testing/regress/ecl/key/stresstext_if.xml b/testing/regress/ecl/key/stresstext_if.xml
new file mode 100644
index 00000000000..b828a1cf43a
--- /dev/null
+++ b/testing/regress/ecl/key/stresstext_if.xml
@@ -0,0 +1,6 @@
+
+ true
+
+
+ Done
+
diff --git a/testing/regress/ecl/setup/files.ecl b/testing/regress/ecl/setup/files.ecl
index 30eebd5c923..98221f09810 100644
--- a/testing/regress/ecl/setup/files.ecl
+++ b/testing/regress/ecl/setup/files.ecl
@@ -209,6 +209,7 @@ EXPORT NameSearchSource := indexPrefix + 'searchSource';
EXPORT getWordIndex() := INDEX(TS.textSearchIndex, NameWordIndex());
EXPORT getSearchIndex() := INDEX(TS.textSearchIndex, NameSearchIndex);
EXPORT getSearchIndexVariant(string variant) := INDEX(TS.textSearchIndex, NameSearchIndex + IF(variant != '', '_' + variant, ''));
+EXPORT getOptSearchIndexVariant(string variant) := INDEX(TS.textSearchIndex, NameSearchIndex + IF(variant != '', '_' + variant, ''), OPT);
EXPORT getSearchSuperIndex() := INDEX(TS.textSearchIndex, '{' + NameSearchIndex + ',' + NameWordIndex() + '}');
EXPORT getSearchSource() := DATASET(NameSearchSource, TS.textSourceRecord, THOR);
diff --git a/testing/regress/ecl/stresstext_if.ecl b/testing/regress/ecl/stresstext_if.ecl
new file mode 100644
index 00000000000..3cff2788ad6
--- /dev/null
+++ b/testing/regress/ecl/stresstext_if.ecl
@@ -0,0 +1,117 @@
+/*##############################################################################
+
+ 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.
+############################################################################## */
+
+//nothor
+//nohthor
+
+//version multiPart=false
+//version multiPart=true
+//version multiPart=true,variant='inplace'
+//version multiPart=true,variant='default'
+//version multiPart=true,variant='inplace',conditionVersion=2
+//version multiPart=true,variant='inplace',conditionVersion=3
+//version multiPart=true,variant='',conditionVersion=2
+
+//The following is processed correctly by the code generator, but not yet supported by roxie
+//enable the test once the necessary changes are made in the roxie engine.
+//noversion multiPart=true,variant='',conditionVersion=4
+
+// The settings below may be useful when trying to analyse Roxie keyed join behaviour, as they will
+// eliminate some wait time for an agent queue to become available
+
+//#option('roxie:minPayloadSize', 10000)
+//#option('roxie:agentThreads', 400)
+//#option('roxie:prestartAgentThreads', true)
+
+import ^ as root;
+multiPart := #IFDEFINED(root.multiPart, true);
+variant := #IFDEFINED(root.variant, 'inplace') : stored('variant');
+numJoins := #IFDEFINED(root.numJoins, 1);
+
+conditionVersion := #IFDEFINED(root.conditionVersion, 1);
+
+#option ('allowActivityForKeyedJoin', true);
+#onwarning (4523, ignore);
+
+trueExpr := true : stored('true');
+
+//--- end of version configuration ---
+
+import $.setup;
+files := setup.files(multiPart, false);
+
+
+createSample(unsigned i, unsigned num, unsigned numRows) := FUNCTION
+
+ //Add a keyed filter to ensure that no splitter is generated.
+ //The splitter performs pathologically on roxie - it may be worth further investigation
+ filtered := files.getSearchSource()(HASH32(kind, word, doc, segment, wpos) % num = i, keyed(word != ''));
+ inputFile := choosen(filtered, numRows);
+ keyFile1 := CASE(variant,
+ 'inplace' => files.getSearchIndexVariant('inplace'),
+ 'default' => files.getSearchIndexVariant('default'),
+ files.getOptSearchIndexVariant('doesnotexist')
+ );
+ keyFile2 := MAP(variant = 'inplace' => files.getSearchIndexVariant('inplace'),
+ variant = 'default' => files.getSearchIndexVariant('default'),
+ files.getOptSearchIndexVariant('doesnotexist')
+ );
+ keyFile3 := IF(variant = 'inplace', files.getSearchIndexVariant('inplace'),
+ files.getOptSearchIndexVariant('doesnotexist')
+ );
+
+ keyFile4 := MAP(variant = 'inplace' => files.getSearchIndexVariant('inplace'),
+ variant = 'default' => files.getSearchIndexVariant('default'),
+ files.getSearchIndexVariant('inplace')(false)
+ );
+#if (conditionVersion = 1)
+ keyFile := keyFile1;
+#elif (conditionVersion = 2)
+ keyFile := keyFile2;
+#elif (conditionVersion = 3)
+ keyFile := keyFile3;
+#else
+ keyFile := keyFile4;
+#end
+
+ j := JOIN(inputFile, keyFile,
+ (LEFT.kind = RIGHT.kind) AND
+ (LEFT.word = RIGHT.word) AND
+ (LEFT.doc = RIGHT.doc) AND
+ (LEFT.segment = RIGHT.segment) AND
+ (LEFT.wpos = RIGHT.wpos), ATMOST(10), KEYED);
+ RETURN NOFOLD(j);
+END;
+
+createSamples(iters, numRows) := FUNCTIONMACRO
+ expectedCount := IF(variant != '', numRows, 0);
+ o := PARALLEL(
+ #DECLARE (count)
+ #SET (count, 0)
+ #LOOP
+ #IF (%count%>=iters)
+ #BREAK
+ #END
+ output(count(createSample(%count%, iters, numRows)) = expectedCount),
+ #SET (count, %count%+1)
+ #END
+ output('Done')
+ );
+ RETURN o;
+ENDMACRO;
+
+createSamples(numJoins, 60000);