|
@@ -249,78 +249,6 @@ static IHqlExpression * createResultName(IHqlExpression * name)
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
-void extractAtmostArgs(IHqlExpression * atmost, SharedHqlExpr & atmostCond, SharedHqlExpr & atmostLimit)
|
|
|
|
-{
|
|
|
|
- atmostLimit.set(queryZero());
|
|
|
|
- if (atmost)
|
|
|
|
- {
|
|
|
|
- IHqlExpression * arg0 = atmost->queryChild(0);
|
|
|
|
- if (arg0->isBoolean())
|
|
|
|
- {
|
|
|
|
- atmostCond.set(arg0);
|
|
|
|
- atmostLimit.set(atmost->queryChild(1));
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- atmostLimit.set(arg0);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static bool matchesAtmostCondition(IHqlExpression * cond, HqlExprArray & atConds, unsigned & numMatched)
|
|
|
|
-{
|
|
|
|
- if (atConds.find(*cond) != NotFound)
|
|
|
|
- {
|
|
|
|
- numMatched++;
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- if (cond->getOperator() != no_assertkeyed)
|
|
|
|
- return false;
|
|
|
|
- unsigned savedMatched = numMatched;
|
|
|
|
- HqlExprArray conds;
|
|
|
|
- cond->queryChild(0)->unwindList(conds, no_and);
|
|
|
|
- ForEachItemIn(i, conds)
|
|
|
|
- {
|
|
|
|
- if (!matchesAtmostCondition(&conds.item(i), atConds, numMatched))
|
|
|
|
- {
|
|
|
|
- numMatched = savedMatched;
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void HqlCppTranslator::splitFuzzyCondition(IHqlExpression * condition, IHqlExpression * atmostCond, SharedHqlExpr & fuzzy, SharedHqlExpr & hard)
|
|
|
|
-{
|
|
|
|
- if (atmostCond)
|
|
|
|
- {
|
|
|
|
- //If join condition has evaluated to a constant then allow any atmost condition.
|
|
|
|
- if (!condition->isConstant())
|
|
|
|
- {
|
|
|
|
- HqlExprArray conds, atConds;
|
|
|
|
- condition->unwindList(conds, no_and);
|
|
|
|
- atmostCond->unwindList(atConds, no_and);
|
|
|
|
- unsigned numAtmostMatched = 0;
|
|
|
|
- ForEachItemIn(i, conds)
|
|
|
|
- {
|
|
|
|
- IHqlExpression & cur = conds.item(i);
|
|
|
|
- if (matchesAtmostCondition(&cur, atConds, numAtmostMatched))
|
|
|
|
- extendConditionOwn(hard, no_and, LINK(&cur));
|
|
|
|
- else
|
|
|
|
- extendConditionOwn(fuzzy, no_and, LINK(&cur));
|
|
|
|
- }
|
|
|
|
- if (atConds.ordinality() != numAtmostMatched)
|
|
|
|
- {
|
|
|
|
- StringBuffer s;
|
|
|
|
- getExprECL(atmostCond, s);
|
|
|
|
- throwError1(HQLERR_AtmostFailMatchCondition, s.str());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- hard.set(condition);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//---------------------------------------------------------------------------
|
|
|
|
-
|
|
|
|
ColumnToOffsetMap * RecordOffsetMap::queryMapping(IHqlExpression * record, unsigned maxRecordSize)
|
|
ColumnToOffsetMap * RecordOffsetMap::queryMapping(IHqlExpression * record, unsigned maxRecordSize)
|
|
{
|
|
{
|
|
ColumnToOffsetMap * match = find(record);
|
|
ColumnToOffsetMap * match = find(record);
|
|
@@ -11178,7 +11106,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivityPipeThrough(BuildCtx & ctx, IH
|
|
//-- no_join [JOIN] --
|
|
//-- no_join [JOIN] --
|
|
|
|
|
|
/* in parms: NOT linked */
|
|
/* in parms: NOT linked */
|
|
-void HqlCppTranslator::doCompareLeftRight(BuildCtx & ctx, const char * funcname, const DatasetReference & datasetLeft, const DatasetReference & datasetRight, HqlExprArray & left, HqlExprArray & right)
|
|
|
|
|
|
+void HqlCppTranslator::doCompareLeftRight(BuildCtx & ctx, const char * funcname, const DatasetReference & datasetLeft, const DatasetReference & datasetRight, const HqlExprArray & left, const HqlExprArray & right)
|
|
{
|
|
{
|
|
OwnedHqlExpr selSeq = createDummySelectorSequence();
|
|
OwnedHqlExpr selSeq = createDummySelectorSequence();
|
|
OwnedHqlExpr leftList = createValueSafe(no_sortlist, makeSortListType(NULL), left);
|
|
OwnedHqlExpr leftList = createValueSafe(no_sortlist, makeSortListType(NULL), left);
|
|
@@ -11192,7 +11120,7 @@ void HqlCppTranslator::doCompareLeftRight(BuildCtx & ctx, const char * funcname,
|
|
buildCompareMemberLR(ctx, funcname, order, datasetLeft.queryDataset(), datasetRight.queryDataset(), selSeq);
|
|
buildCompareMemberLR(ctx, funcname, order, datasetLeft.queryDataset(), datasetRight.queryDataset(), selSeq);
|
|
}
|
|
}
|
|
|
|
|
|
-void HqlCppTranslator::buildSlidingMatchFunction(BuildCtx & ctx, HqlExprArray & leftEq, HqlExprArray & rightEq, HqlExprArray & slidingMatches, const char * funcname, unsigned childIndex, const DatasetReference & datasetL, const DatasetReference & datasetR)
|
|
|
|
|
|
+void HqlCppTranslator::buildSlidingMatchFunction(BuildCtx & ctx, const HqlExprArray & leftEq, const HqlExprArray & rightEq, const HqlExprArray & slidingMatches, const char * funcname, unsigned childIndex, const DatasetReference & datasetL, const DatasetReference & datasetR)
|
|
{
|
|
{
|
|
HqlExprArray left, right;
|
|
HqlExprArray left, right;
|
|
unsigned numSimple = leftEq.ordinality() - slidingMatches.ordinality();
|
|
unsigned numSimple = leftEq.ordinality() - slidingMatches.ordinality();
|
|
@@ -11211,7 +11139,7 @@ void HqlCppTranslator::buildSlidingMatchFunction(BuildCtx & ctx, HqlExprArray &
|
|
doCompareLeftRight(ctx, funcname, datasetL, datasetR, left, right);
|
|
doCompareLeftRight(ctx, funcname, datasetL, datasetR, left, right);
|
|
}
|
|
}
|
|
|
|
|
|
-void HqlCppTranslator::generateSortCompare(BuildCtx & nestedctx, BuildCtx & ctx, node_operator side, const DatasetReference & dataset, HqlExprArray & sorts, bool canRemoveSort, IHqlExpression * noSortAttr, bool canReuseLeft, bool isLightweight, bool isLocal)
|
|
|
|
|
|
+void HqlCppTranslator::generateSortCompare(BuildCtx & nestedctx, BuildCtx & ctx, node_operator side, const DatasetReference & dataset, const HqlExprArray & sorts, bool canRemoveSort, IHqlExpression * noSortAttr, bool canReuseLeft, bool isLightweight, bool isLocal)
|
|
{
|
|
{
|
|
StringBuffer s, compareName;
|
|
StringBuffer s, compareName;
|
|
|
|
|
|
@@ -11342,7 +11270,7 @@ void HqlCppTranslator::generateSerializeFunction(BuildCtx & ctx, const char * fu
|
|
buildReturnRecordSize(r2kctx, serialize ? tgtCursor : srcCursor);
|
|
buildReturnRecordSize(r2kctx, serialize ? tgtCursor : srcCursor);
|
|
}
|
|
}
|
|
|
|
|
|
-void HqlCppTranslator::generateSerializeKey(BuildCtx & nestedctx, node_operator side, const DatasetReference & dataset, HqlExprArray & sorts, bool isGlobal, bool generateCompares, bool canReuseLeft)
|
|
|
|
|
|
+void HqlCppTranslator::generateSerializeKey(BuildCtx & nestedctx, node_operator side, const DatasetReference & dataset, const HqlExprArray & sorts, bool isGlobal, bool generateCompares, bool canReuseLeft)
|
|
{
|
|
{
|
|
//check if there are any ifblocks, and if so don't allow it. Even more accurate would be no join fields used in ifblocks
|
|
//check if there are any ifblocks, and if so don't allow it. Even more accurate would be no join fields used in ifblocks
|
|
IHqlExpression * record = dataset.queryDataset()->queryRecord();
|
|
IHqlExpression * record = dataset.queryDataset()->queryRecord();
|
|
@@ -11498,6 +11426,37 @@ void HqlCppTranslator::doBuildJoinRowLimitHelper(ActivityInstance & instance, IH
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+static size32_t getMaxSubstringLength(IHqlExpression * expr)
|
|
|
|
+{
|
|
|
|
+ IHqlExpression * rawSelect = expr->queryChild(0);
|
|
|
|
+ IHqlExpression * range = expr->queryChild(1);
|
|
|
|
+ IHqlExpression * rangeLow = range->queryChild(0);
|
|
|
|
+ unsigned rawLength = rawSelect->queryType()->getStringLen();
|
|
|
|
+ if (matchesConstantValue(rangeLow, 1))
|
|
|
|
+ return rawLength;
|
|
|
|
+
|
|
|
|
+ __int64 lowValue = getIntValue(rangeLow, UNKNOWN_LENGTH);
|
|
|
|
+ size32_t resultLength = UNKNOWN_LENGTH;
|
|
|
|
+ if ((rawLength != UNKNOWN_LENGTH) && (lowValue >= 1) && (lowValue <= rawLength))
|
|
|
|
+ resultLength = rawLength - (size32_t)(lowValue - 1);
|
|
|
|
+ return resultLength;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static IHqlExpression * getSimplifiedCommonSubstringRange(IHqlExpression * expr)
|
|
|
|
+{
|
|
|
|
+ IHqlExpression * rawSelect = expr->queryChild(0);
|
|
|
|
+ IHqlExpression * range = expr->queryChild(1);
|
|
|
|
+ IHqlExpression * rangeLow = range->queryChild(0);
|
|
|
|
+ if (matchesConstantValue(rangeLow, 1))
|
|
|
|
+ return LINK(rawSelect);
|
|
|
|
+
|
|
|
|
+ HqlExprArray args;
|
|
|
|
+ args.append(*LINK(rawSelect));
|
|
|
|
+ args.append(*createValue(no_rangefrom, makeNullType(), LINK(rangeLow)));
|
|
|
|
+ return expr->clone(args);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & ctx, IHqlExpression * expr)
|
|
ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & ctx, IHqlExpression * expr)
|
|
{
|
|
{
|
|
node_operator op = expr->getOperator();
|
|
node_operator op = expr->getOperator();
|
|
@@ -11550,7 +11509,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
bool isLocalJoin = !isHashJoin && expr->hasAttribute(localAtom);
|
|
bool isLocalJoin = !isHashJoin && expr->hasAttribute(localAtom);
|
|
bool joinToSelf = (op == no_selfjoin);
|
|
bool joinToSelf = (op == no_selfjoin);
|
|
bool allowAllToLookupConvert = !options.noAllToLookupConversion;
|
|
bool allowAllToLookupConvert = !options.noAllToLookupConversion;
|
|
- IHqlExpression * atmost = expr->queryAttribute(atmostAtom);
|
|
|
|
|
|
+ IHqlExpression * atmostAttr = expr->queryAttribute(atmostAtom);
|
|
//Delay removing ungroups until this point because they can be useful for reducing the size of spill files.
|
|
//Delay removing ungroups until this point because they can be useful for reducing the size of spill files.
|
|
if (isUngroup(dataset1) && !isLookupJoin)
|
|
if (isUngroup(dataset1) && !isLookupJoin)
|
|
dataset1.set(dataset1->queryChild(0));
|
|
dataset1.set(dataset1->queryChild(0));
|
|
@@ -11575,34 +11534,25 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
- OwnedHqlExpr atmostCond, atmostLimit;
|
|
|
|
- extractAtmostArgs(atmost, atmostCond, atmostLimit);
|
|
|
|
-
|
|
|
|
- HqlExprArray leftSorts, rightSorts, slidingMatches;
|
|
|
|
bool slidingAllowed = options.slidingJoins && canBeSlidingJoin(expr);
|
|
bool slidingAllowed = options.slidingJoins && canBeSlidingJoin(expr);
|
|
- OwnedHqlExpr match;
|
|
|
|
-
|
|
|
|
- OwnedHqlExpr fuzzy, hard;
|
|
|
|
- bool isLimitedSubstringJoin;
|
|
|
|
- splitFuzzyCondition(condition, atmostCond, fuzzy, hard);
|
|
|
|
- match.setown(findJoinSortOrders(hard, dataset1, dataset2, selSeq, leftSorts, rightSorts, isLimitedSubstringJoin, slidingAllowed ? &slidingMatches : NULL));
|
|
|
|
|
|
+ JoinSortInfo joinInfo;
|
|
|
|
+ joinInfo.findJoinSortOrders(expr, slidingAllowed);
|
|
|
|
|
|
- if (atmost && match)
|
|
|
|
|
|
+ if (atmostAttr && !joinInfo.conditionAllEqualities)
|
|
{
|
|
{
|
|
if (isAllJoin)
|
|
if (isAllJoin)
|
|
allowAllToLookupConvert = false;
|
|
allowAllToLookupConvert = false;
|
|
else
|
|
else
|
|
{
|
|
{
|
|
StringBuffer s;
|
|
StringBuffer s;
|
|
- throwError1(HQLERR_BadJoinConditionAtMost,getExprECL(match, s.append(" (")).append(")").str());
|
|
|
|
|
|
+ throwError1(HQLERR_BadJoinConditionAtMost,getExprECL(joinInfo.extraMatch, s.append(" (")).append(")").str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- extendConditionOwn(match, no_and, fuzzy.getClear());
|
|
|
|
|
|
|
|
LinkedHqlExpr rhs = dataset2;
|
|
LinkedHqlExpr rhs = dataset2;
|
|
if (isAllJoin)
|
|
if (isAllJoin)
|
|
{
|
|
{
|
|
- if (leftSorts.ordinality() && allowAllToLookupConvert)
|
|
|
|
|
|
+ if (joinInfo.hasRequiredEqualities() && allowAllToLookupConvert)
|
|
{
|
|
{
|
|
//Convert an all join to a many lookup if it can be done that way - more efficient, and same resourcing/semantics ...
|
|
//Convert an all join to a many lookup if it can be done that way - more efficient, and same resourcing/semantics ...
|
|
isManyLookup = true;
|
|
isManyLookup = true;
|
|
@@ -11610,7 +11560,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
isLookupJoin = true;
|
|
isLookupJoin = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- else if (leftSorts.ordinality() == 0)
|
|
|
|
|
|
+ else if (!joinInfo.hasRequiredEqualities() && !joinInfo.hasOptionalEqualities())
|
|
{
|
|
{
|
|
if (expr->hasAttribute(_conditionFolded_Atom))
|
|
if (expr->hasAttribute(_conditionFolded_Atom))
|
|
{
|
|
{
|
|
@@ -11766,19 +11716,19 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
//more could use the compareLeftRight function instead of generating the same code
|
|
//more could use the compareLeftRight function instead of generating the same code
|
|
//several time....
|
|
//several time....
|
|
}
|
|
}
|
|
- bool canReuseLeft = recordTypesMatch(dataset1, dataset2) && arraysMatch(leftSorts, rightSorts);
|
|
|
|
|
|
+ bool canReuseLeftCompare = recordTypesMatch(dataset1, dataset2) && arraysMatch(joinInfo.queryLeftSort(), joinInfo.queryRightSort());
|
|
if (!isAllJoin)
|
|
if (!isAllJoin)
|
|
{
|
|
{
|
|
bool isLocalSort = isLocalJoin || !targetThor();
|
|
bool isLocalSort = isLocalJoin || !targetThor();
|
|
//Lookup join doesn't need the left sort (unless it is reused elsewhere), or the right sort unless it is deduping.
|
|
//Lookup join doesn't need the left sort (unless it is reused elsewhere), or the right sort unless it is deduping.
|
|
- if (canReuseLeft || !isLookupJoin)
|
|
|
|
- generateSortCompare(instance->nestedctx, instance->classctx, no_left, lhsDsRef, leftSorts, true, noSortAttr, false, isLightweight, isLocalSort);
|
|
|
|
|
|
+ if (canReuseLeftCompare || !isLookupJoin)
|
|
|
|
+ generateSortCompare(instance->nestedctx, instance->classctx, no_left, lhsDsRef, joinInfo.queryLeftSort(), true, noSortAttr, false, isLightweight, isLocalSort);
|
|
if (!(isLookupJoin && isManyLookup && !couldBeKeepOne && !targetThor())) // many lookup doesn't need to dedup the rhs
|
|
if (!(isLookupJoin && isManyLookup && !couldBeKeepOne && !targetThor())) // many lookup doesn't need to dedup the rhs
|
|
- generateSortCompare(instance->nestedctx, instance->classctx, no_right, rhsDsRef, rightSorts, isLocalSort, noSortAttr, canReuseLeft, isLightweight, isLocalSort);
|
|
|
|
|
|
+ generateSortCompare(instance->nestedctx, instance->classctx, no_right, rhsDsRef, joinInfo.queryRightSort(), isLocalSort, noSortAttr, canReuseLeftCompare, isLightweight, isLocalSort);
|
|
|
|
|
|
bool isGlobal = !isLocalJoin && !instance->isChildActivity();
|
|
bool isGlobal = !isLocalJoin && !instance->isChildActivity();
|
|
- generateSerializeKey(instance->nestedctx, no_left, lhsDsRef, leftSorts, isGlobal, false, false);
|
|
|
|
- generateSerializeKey(instance->nestedctx, no_right, rhsDsRef, rightSorts, isGlobal, false, canReuseLeft);
|
|
|
|
|
|
+ generateSerializeKey(instance->nestedctx, no_left, lhsDsRef, joinInfo.queryLeftSort(), isGlobal, false, false);
|
|
|
|
+ generateSerializeKey(instance->nestedctx, no_right, rhsDsRef, joinInfo.queryRightSort(), isGlobal, false, canReuseLeftCompare);
|
|
}
|
|
}
|
|
|
|
|
|
StringBuffer flags;
|
|
StringBuffer flags;
|
|
@@ -11797,8 +11747,8 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
flags.append("|JFmatchAbortLimitSkips");
|
|
flags.append("|JFmatchAbortLimitSkips");
|
|
if (rowlimit && rowlimit->hasAttribute(countAtom))
|
|
if (rowlimit && rowlimit->hasAttribute(countAtom))
|
|
flags.append("|JFcountmatchabortlimit");
|
|
flags.append("|JFcountmatchabortlimit");
|
|
- if (slidingMatches.ordinality()) flags.append("|JFslidingmatch");
|
|
|
|
- if (match) flags.append("|JFmatchrequired");
|
|
|
|
|
|
+ if (joinInfo.slidingMatches.ordinality()) flags.append("|JFslidingmatch");
|
|
|
|
+ if (joinInfo.extraMatch) flags.append("|JFmatchrequired");
|
|
if (isLookupJoin && isManyLookup) flags.append("|JFmanylookup");
|
|
if (isLookupJoin && isManyLookup) flags.append("|JFmanylookup");
|
|
if (expr->hasAttribute(onFailAtom))
|
|
if (expr->hasAttribute(onFailAtom))
|
|
flags.append("|JFonfail");
|
|
flags.append("|JFonfail");
|
|
@@ -11806,12 +11756,12 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
flags.append("|JFreorderable");
|
|
flags.append("|JFreorderable");
|
|
if (transformReturnsSide(expr, no_left, 0))
|
|
if (transformReturnsSide(expr, no_left, 0))
|
|
flags.append("|JFtransformmatchesleft");
|
|
flags.append("|JFtransformmatchesleft");
|
|
- if (isLimitedSubstringJoin)
|
|
|
|
|
|
+ if (joinInfo.hasOptionalEqualities())
|
|
flags.append("|JFlimitedprefixjoin");
|
|
flags.append("|JFlimitedprefixjoin");
|
|
|
|
|
|
- if (isAlreadySorted(dataset1, leftSorts, true, true) || userPreventsSort(noSortAttr, no_left))
|
|
|
|
|
|
+ if (isAlreadySorted(dataset1, joinInfo.queryLeftSort(), true, true) || userPreventsSort(noSortAttr, no_left))
|
|
flags.append("|JFleftSortedLocally");
|
|
flags.append("|JFleftSortedLocally");
|
|
- if (isAlreadySorted(dataset2, rightSorts, true, true) || userPreventsSort(noSortAttr, no_right))
|
|
|
|
|
|
+ if (isAlreadySorted(dataset2, joinInfo.queryRightSort(), true, true) || userPreventsSort(noSortAttr, no_right))
|
|
flags.append("|JFrightSortedLocally");
|
|
flags.append("|JFrightSortedLocally");
|
|
|
|
|
|
if (flags.length())
|
|
if (flags.length())
|
|
@@ -11821,8 +11771,8 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
{
|
|
{
|
|
buildSkewThresholdMembers(instance->classctx, expr);
|
|
buildSkewThresholdMembers(instance->classctx, expr);
|
|
|
|
|
|
- if (!isZero(atmostLimit))
|
|
|
|
- doBuildUnsignedFunction(instance->startctx, "getJoinLimit", atmostLimit);
|
|
|
|
|
|
+ if (!isZero(joinInfo.atmost.limit))
|
|
|
|
+ doBuildUnsignedFunction(instance->startctx, "getJoinLimit", joinInfo.atmost.limit);
|
|
}
|
|
}
|
|
|
|
|
|
if (keepLimit)
|
|
if (keepLimit)
|
|
@@ -11878,21 +11828,10 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
if (!isAllJoin)
|
|
if (!isAllJoin)
|
|
{
|
|
{
|
|
//if left and right match, then leftright compare function is also the same
|
|
//if left and right match, then leftright compare function is also the same
|
|
- if (isLimitedSubstringJoin)
|
|
|
|
- {
|
|
|
|
- HqlExprArray compareLeftSorts, compareRightSorts;
|
|
|
|
- unsigned max = leftSorts.ordinality()-1;
|
|
|
|
- for (unsigned i=0; i < max; i++)
|
|
|
|
- {
|
|
|
|
- compareLeftSorts.append(OLINK(leftSorts.item(i)));
|
|
|
|
- compareRightSorts.append(OLINK(rightSorts.item(i)));
|
|
|
|
- }
|
|
|
|
- doCompareLeftRight(instance->nestedctx, "CompareLeftRight", lhsDsRef, rhsDsRef, compareLeftSorts, compareRightSorts);
|
|
|
|
- }
|
|
|
|
- else if (canReuseLeft)
|
|
|
|
|
|
+ if (canReuseLeftCompare && !joinInfo.hasOptionalEqualities())
|
|
instance->nestedctx.addQuoted("virtual ICompare * queryCompareLeftRight() { return &compareLeft; }");
|
|
instance->nestedctx.addQuoted("virtual ICompare * queryCompareLeftRight() { return &compareLeft; }");
|
|
else
|
|
else
|
|
- doCompareLeftRight(instance->nestedctx, "CompareLeftRight", lhsDsRef, rhsDsRef, leftSorts, rightSorts);
|
|
|
|
|
|
+ doCompareLeftRight(instance->nestedctx, "CompareLeftRight", lhsDsRef, rhsDsRef, joinInfo.queryLeftReq(), joinInfo.queryRightReq());
|
|
}
|
|
}
|
|
|
|
|
|
doBuildJoinRowLimitHelper(*instance, rowlimit, NULL, false);
|
|
doBuildJoinRowLimitHelper(*instance, rowlimit, NULL, false);
|
|
@@ -11903,44 +11842,111 @@ ABoundActivity * HqlCppTranslator::doBuildActivityJoinOrDenormalize(BuildCtx & c
|
|
buildClearRecordMember(instance->createctx, "Left", dataset1);
|
|
buildClearRecordMember(instance->createctx, "Left", dataset1);
|
|
if (createDefaultRight)
|
|
if (createDefaultRight)
|
|
buildClearRecordMember(instance->createctx, "Right", dataset2);
|
|
buildClearRecordMember(instance->createctx, "Right", dataset2);
|
|
- buildJoinMatchFunction(instance->startctx, "match", dataset1, dataset2, match, selSeq);
|
|
|
|
|
|
+ buildJoinMatchFunction(instance->startctx, "match", dataset1, dataset2, joinInfo.extraMatch, selSeq);
|
|
|
|
|
|
- if (slidingMatches.ordinality())
|
|
|
|
|
|
+ if (joinInfo.slidingMatches.ordinality())
|
|
{
|
|
{
|
|
- buildSlidingMatchFunction(instance->nestedctx, leftSorts, rightSorts, slidingMatches, "CompareLeftRightLower", 1, lhsDsRef, rhsDsRef);
|
|
|
|
- buildSlidingMatchFunction(instance->nestedctx, leftSorts, rightSorts, slidingMatches, "CompareLeftRightUpper", 2, lhsDsRef, rhsDsRef);
|
|
|
|
|
|
+ buildSlidingMatchFunction(instance->nestedctx, joinInfo.queryLeftSort(), joinInfo.queryRightSort(), joinInfo.slidingMatches, "CompareLeftRightLower", 1, lhsDsRef, rhsDsRef);
|
|
|
|
+ buildSlidingMatchFunction(instance->nestedctx, joinInfo.queryLeftSort(), joinInfo.queryRightSort(), joinInfo.slidingMatches, "CompareLeftRightUpper", 2, lhsDsRef, rhsDsRef);
|
|
}
|
|
}
|
|
|
|
|
|
if (isHashJoin||isLookupJoin)
|
|
if (isHashJoin||isLookupJoin)
|
|
{
|
|
{
|
|
- OwnedHqlExpr leftList = createValueSafe(no_sortlist, makeSortListType(NULL), leftSorts);
|
|
|
|
|
|
+ OwnedHqlExpr leftList = createValueSafe(no_sortlist, makeSortListType(NULL), joinInfo.queryLeftReq());
|
|
buildHashOfExprsClass(instance->nestedctx, "HashLeft", leftList, lhsDsRef, false);
|
|
buildHashOfExprsClass(instance->nestedctx, "HashLeft", leftList, lhsDsRef, false);
|
|
|
|
|
|
- if (!canReuseLeft)
|
|
|
|
|
|
+ bool canReuseLeftHash = recordTypesMatch(dataset1, dataset2) && arraysMatch(joinInfo.queryLeftReq(), joinInfo.queryRightReq());
|
|
|
|
+ if (!canReuseLeftHash)
|
|
{
|
|
{
|
|
- OwnedHqlExpr rightList = createValueSafe(no_sortlist, makeSortListType(NULL), rightSorts);
|
|
|
|
|
|
+ OwnedHqlExpr rightList = createValueSafe(no_sortlist, makeSortListType(NULL), joinInfo.queryRightReq());
|
|
buildHashOfExprsClass(instance->nestedctx, "HashRight", rightList, rhsDsRef, false);
|
|
buildHashOfExprsClass(instance->nestedctx, "HashRight", rightList, rhsDsRef, false);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
instance->nestedctx.addQuoted("virtual IHash * queryHashRight() { return &HashLeft; }");
|
|
instance->nestedctx.addQuoted("virtual IHash * queryHashRight() { return &HashLeft; }");
|
|
}
|
|
}
|
|
|
|
|
|
- if (isLimitedSubstringJoin)
|
|
|
|
|
|
+ if (joinInfo.hasOptionalEqualities())
|
|
{
|
|
{
|
|
OwnedHqlExpr leftSelect = createSelector(no_left, dataset1, selSeq);
|
|
OwnedHqlExpr leftSelect = createSelector(no_left, dataset1, selSeq);
|
|
OwnedHqlExpr rightSelect = createSelector(no_right, dataset2, selSeq);
|
|
OwnedHqlExpr rightSelect = createSelector(no_right, dataset2, selSeq);
|
|
- HqlExprArray args;
|
|
|
|
- args.append(*lhsDsRef.mapCompound(&leftSorts.tos(), leftSelect));
|
|
|
|
- args.append(*rhsDsRef.mapCompound(&rightSorts.tos(), rightSelect));
|
|
|
|
|
|
|
|
- IIdAtom * func = prefixDiffStrId;
|
|
|
|
- ITypeInfo * lhsType = args.item(0).queryType();
|
|
|
|
- if (isUnicodeType(lhsType))
|
|
|
|
|
|
+ UnsignedArray origins;
|
|
|
|
+ unsigned origin = 0;
|
|
|
|
+ ForEachItemIn(i, joinInfo.queryLeftOpt())
|
|
{
|
|
{
|
|
- func = prefixDiffUnicodeId;
|
|
|
|
- args.append(*createConstant(lhsType->queryLocale()->str()));
|
|
|
|
|
|
+ IHqlExpression & left = joinInfo.queryLeftOpt().item(i);
|
|
|
|
+ IHqlExpression & right = joinInfo.queryRightOpt().item(i);
|
|
|
|
+ unsigned delta;
|
|
|
|
+ if (origin == UNKNOWN_LENGTH)
|
|
|
|
+ throwError(HQLERR_AtmostFollowUnknownSubstr);
|
|
|
|
+
|
|
|
|
+ if (isCommonSubstringRange(&left))
|
|
|
|
+ {
|
|
|
|
+ size32_t leftLen = getMaxSubstringLength(&left);
|
|
|
|
+ size32_t rightLen = getMaxSubstringLength(&right);
|
|
|
|
+ if (leftLen == rightLen)
|
|
|
|
+ delta = leftLen;
|
|
|
|
+ else
|
|
|
|
+ delta = UNKNOWN_LENGTH;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ delta = 1;
|
|
|
|
+ origins.append(origin);
|
|
|
|
+ if (delta != UNKNOWN_LENGTH)
|
|
|
|
+ origin += delta;
|
|
|
|
+ else
|
|
|
|
+ origin = UNKNOWN_LENGTH;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ OwnedHqlExpr compare;
|
|
|
|
+ OwnedITypeInfo retType = makeIntType(4, true);
|
|
|
|
+ OwnedHqlExpr zero = createConstant(retType->castFrom(true, 0));
|
|
|
|
+ ForEachItemInRev(i1, joinInfo.queryLeftOpt())
|
|
|
|
+ {
|
|
|
|
+ IHqlExpression & left = joinInfo.queryLeftOpt().item(i1);
|
|
|
|
+ IHqlExpression & right = joinInfo.queryRightOpt().item(i1);
|
|
|
|
+
|
|
|
|
+ unsigned origin = origins.item(i1);
|
|
|
|
+ if (isCommonSubstringRange(&left))
|
|
|
|
+ {
|
|
|
|
+ OwnedHqlExpr simpleLeft = getSimplifiedCommonSubstringRange(&left);
|
|
|
|
+ OwnedHqlExpr simpleRight = getSimplifiedCommonSubstringRange(&right);
|
|
|
|
+ HqlExprArray args;
|
|
|
|
+ args.append(*lhsDsRef.mapCompound(simpleLeft, leftSelect));
|
|
|
|
+ args.append(*rhsDsRef.mapCompound(simpleRight, rightSelect));
|
|
|
|
+
|
|
|
|
+ IIdAtom * func = prefixDiffStrId;
|
|
|
|
+ ITypeInfo * lhsType = args.item(0).queryType();
|
|
|
|
+ if (isUnicodeType(lhsType))
|
|
|
|
+ {
|
|
|
|
+ func = prefixDiffUnicodeId;
|
|
|
|
+ args.append(*createConstant(lhsType->queryLocale()->str()));
|
|
|
|
+ }
|
|
|
|
+ args.append(*getSizetConstant(origin));
|
|
|
|
+ OwnedHqlExpr diff = bindFunctionCall(func, args);
|
|
|
|
+ if (compare)
|
|
|
|
+ {
|
|
|
|
+ OwnedHqlExpr alias = createAlias(diff, NULL);
|
|
|
|
+ OwnedHqlExpr compareNe = createValue(no_ne, makeBoolType(), LINK(alias), LINK(zero));
|
|
|
|
+ compare.setown(createValue(no_if, LINK(retType), compareNe.getClear(), LINK(alias), compare.getClear()));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ compare.set(diff);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ OwnedHqlExpr leftExpr = lhsDsRef.mapCompound(&left, leftSelect);
|
|
|
|
+ OwnedHqlExpr rightExpr = lhsDsRef.mapCompound(&right, rightSelect);
|
|
|
|
+ OwnedHqlExpr compareGt = createValue(no_gt, makeBoolType(), LINK(leftExpr), LINK(rightExpr));
|
|
|
|
+ OwnedHqlExpr gtValue = createConstant(retType->castFrom(true, origin+1));
|
|
|
|
+ OwnedHqlExpr ltValue = createConstant(retType->castFrom(true, -(int)(origin+1)));
|
|
|
|
+ OwnedHqlExpr mismatch = createValue(no_if, LINK(retType), compareGt.getClear(), gtValue.getClear(), ltValue.getClear());
|
|
|
|
+ OwnedHqlExpr compareNe = createValue(no_ne, makeBoolType(), LINK(leftExpr), LINK(rightExpr));
|
|
|
|
+ OwnedHqlExpr eqValue = compare ? LINK(compare) : LINK(zero);
|
|
|
|
+ compare.setown(createValue(no_if, LINK(retType), compareNe.getClear(), mismatch.getClear(), eqValue.getClear()));
|
|
|
|
+ origin += 1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- OwnedHqlExpr compare = bindFunctionCall(func, args);
|
|
|
|
|
|
|
|
buildCompareMemberLR(instance->nestedctx, "PrefixCompare", compare, dataset1, dataset2, selSeq);
|
|
buildCompareMemberLR(instance->nestedctx, "PrefixCompare", compare, dataset1, dataset2, selSeq);
|
|
}
|
|
}
|