Skip to content

Commit b1c3672

Browse files
committed
pushdown substring match functions (#18459)
1 parent c6746fb commit b1c3672

File tree

8 files changed

+87
-47
lines changed

8 files changed

+87
-47
lines changed

ydb/core/kqp/compile_service/kqp_compile_actor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ void ApplyServiceConfig(TKikimrConfiguration& kqpConfig, const TTableServiceConf
655655
kqpConfig.EnableSpilling = serviceConfig.GetEnableQueryServiceSpilling();
656656
kqpConfig.EnableSpillingInHashJoinShuffleConnections = serviceConfig.GetEnableSpillingInHashJoinShuffleConnections();
657657
kqpConfig.EnableOlapScalarApply = serviceConfig.GetEnableOlapScalarApply();
658+
kqpConfig.EnableOlapSubstringPushdown = serviceConfig.GetEnableOlapSubstringPushdown();
658659

659660
if (const auto limit = serviceConfig.GetResourceManager().GetMkqlHeavyProgramMemoryLimit()) {
660661
kqpConfig._KqpYqlCombinerMemoryLimit = std::max(1_GB, limit - (limit >> 2U));

ydb/core/kqp/compile_service/kqp_compile_service.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ class TKqpCompileService : public TActorBootstrapped<TKqpCompileService> {
320320

321321
bool enableSpillingInHashJoinShuffleConnections = TableServiceConfig.GetEnableSpillingInHashJoinShuffleConnections();
322322
bool enableOlapScalarApply = TableServiceConfig.GetEnableOlapScalarApply();
323+
bool enableOlapSubstringPushdown = TableServiceConfig.GetEnableOlapSubstringPushdown();
323324

324325
TableServiceConfig.Swap(event.MutableConfig()->MutableTableServiceConfig());
325326
LOG_INFO(*TlsActivationContext, NKikimrServices::KQP_COMPILE_SERVICE, "Updated config");
@@ -355,7 +356,8 @@ class TKqpCompileService : public TActorBootstrapped<TKqpCompileService> {
355356
TableServiceConfig.GetEnableQueryServiceSpilling() != enableSpilling ||
356357
TableServiceConfig.GetDefaultEnableShuffleElimination() != defaultEnableShuffleElimination ||
357358
TableServiceConfig.GetEnableSpillingInHashJoinShuffleConnections() != enableSpillingInHashJoinShuffleConnections ||
358-
TableServiceConfig.GetEnableOlapScalarApply() != enableOlapScalarApply
359+
TableServiceConfig.GetEnableOlapScalarApply() != enableOlapScalarApply ||
360+
TableServiceConfig.GetEnableOlapSubstringPushdown() != enableOlapSubstringPushdown
359361
)
360362
{
361363

ydb/core/kqp/opt/physical/kqp_opt_phy_olap_filter.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,10 @@ std::pair<TVector<TOLAPPredicateNode>, TVector<TOLAPPredicateNode>> SplitForPart
736736
TExprBase KqpPushOlapFilter(TExprBase node, TExprContext& ctx, const TKqpOptimizeContext& kqpCtx,
737737
TTypeAnnotationContext& typesCtx)
738738
{
739-
const bool allowApply = kqpCtx.Config->EnableOlapScalarApply;
739+
const auto pushdownOptions = TPushdownOptions{
740+
kqpCtx.Config->EnableOlapScalarApply,
741+
kqpCtx.Config->EnableOlapSubstringPushdown
742+
};
740743
if (!kqpCtx.Config->HasOptEnableOlapPushdown()) {
741744
return node;
742745
}
@@ -768,16 +771,16 @@ TExprBase KqpPushOlapFilter(TExprBase node, TExprContext& ctx, const TKqpOptimiz
768771

769772
TOLAPPredicateNode predicateTree;
770773
predicateTree.ExprNode = predicate.Ptr();
771-
CollectPredicates(predicate, predicateTree, &lambdaArg, read.Process().Body(), false);
774+
CollectPredicates(predicate, predicateTree, &lambdaArg, read.Process().Body(), pushdownOptions);
772775
YQL_ENSURE(predicateTree.IsValid(), "Collected OLAP predicates are invalid");
773776

774777
auto [pushable, remaining] = SplitForPartialPushdown(predicateTree, false);
775778
TVector<TFilterOpsLevels> pushedPredicates;
776779
for (const auto& p: pushable) {
777-
pushedPredicates.emplace_back(PredicatePushdown(TExprBase(p.ExprNode), lambdaArg, ctx, node.Pos(), allowApply));
780+
pushedPredicates.emplace_back(PredicatePushdown(TExprBase(p.ExprNode), lambdaArg, ctx, node.Pos(), pushdownOptions.AllowOlapApply));
778781
}
779782

780-
if (allowApply) {
783+
if (pushdownOptions.AllowOlapApply) {
781784
TVector<TOLAPPredicateNode> remainingAfterApply;
782785
for(const auto& p: remaining) {
783786
const auto recoveredOptinalIfForNonPushedDownPredicates = Build<TCoOptionalIf>(ctx, node.Pos())
@@ -797,13 +800,13 @@ TExprBase KqpPushOlapFilter(TExprBase node, TExprContext& ctx, const TKqpOptimiz
797800

798801
TOLAPPredicateNode predicateTree;
799802
predicateTree.ExprNode = predicate.Ptr();
800-
CollectPredicates(predicate, predicateTree, &lambdaArg, read.Process().Body(), true);
803+
CollectPredicates(predicate, predicateTree, &lambdaArg, read.Process().Body(), {true, pushdownOptions.PushdownSubstring});
801804

802805
YQL_ENSURE(predicateTree.IsValid(), "Collected OLAP predicates are invalid");
803806
auto [pushable, remaining] = SplitForPartialPushdown(predicateTree, true);
804807
for (const auto& p: pushable) {
805808
if (p.CanBePushed) {
806-
auto pred = PredicatePushdown(TExprBase(p.ExprNode), lambdaArg, ctx, node.Pos(), allowApply);
809+
auto pred = PredicatePushdown(TExprBase(p.ExprNode), lambdaArg, ctx, node.Pos(), pushdownOptions.AllowOlapApply);
807810
pushedPredicates.emplace_back(pred);
808811
}
809812
else {

ydb/core/kqp/opt/physical/predicate_collector.cpp

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ bool IsSupportedDataType(const TCoDataCtor& node, bool allowOlapApply) {
4949
return false;
5050
}
5151

52-
bool IsSupportedCast(const TCoSafeCast& cast, bool allowOlapApply=false) {
52+
bool IsSupportedCast(const TCoSafeCast& cast, bool allowOlapApply) {
5353
auto maybeDataType = cast.Type().Maybe<TCoDataType>();
5454
if (!maybeDataType) {
5555
if (const auto maybeOptionalType = cast.Type().Maybe<TCoOptionalType>()) {
@@ -111,8 +111,26 @@ bool IsGoodTypeForComparsionPushdown(const TTypeAnnotationNode& type, bool allow
111111
NUdf::EDataTypeFeatures::TimeIntervalType) & features) && !(NUdf::EDataTypeFeatures::TzDateType & features)));
112112
}
113113

114+
bool CanPushdownStringUdf(const TExprNode& udf, bool pushdownSubstring) {
115+
if (!pushdownSubstring) {
116+
return false;
117+
}
118+
const auto& name = udf.Head().Content();
119+
static const THashSet<TString> substringMatchUdfs = {
120+
"String.AsciiEqualsIgnoreCase",
121+
122+
"String.Contains",
123+
"String.AsciiContainsIgnoreCase",
124+
"String.StartsWith",
125+
"String.AsciiStartsWithIgnoreCase",
126+
"String.EndsWith",
127+
"String.AsciiEndsWithIgnoreCase"
128+
};
129+
return substringMatchUdfs.contains(name);
130+
}
131+
114132
[[maybe_unused]]
115-
bool AbstractTreeCanBePushed(const TExprBase& expr, const TExprNode* ) {
133+
bool AbstractTreeCanBePushed(const TExprBase& expr, const TExprNode*, bool pushdownSubstring) {
116134
if (!expr.Ref().IsCallable({"Apply", "Coalesce", "NamedApply", "IfPresent", "Visit"})) {
117135
return false;
118136
}
@@ -132,7 +150,7 @@ bool AbstractTreeCanBePushed(const TExprBase& expr, const TExprNode* ) {
132150
for (const auto& apply : applies) {
133151
const auto& udf = SkipCallables(apply->Head(), {"AssumeStrict"});
134152
const auto& udfName = udf.Head();
135-
if (!(udfName.Content().starts_with("Json2.") || udfName.Content().starts_with("Re2."))) {
153+
if (!(udfName.Content().starts_with("Json2.") || udfName.Content().starts_with("Re2.") || CanPushdownStringUdf(udf, pushdownSubstring))) {
136154
return false;
137155
}
138156

@@ -159,8 +177,8 @@ bool IfPresentCanBePushed(const TCoIfPresent& ifPresent, const TExprNode* lambda
159177
return allowOlapApply;
160178
}
161179

162-
bool CheckExpressionNodeForPushdown(const TExprBase& node, const TExprNode* lambdaArg, bool allowOlapApply) {
163-
if (allowOlapApply) {
180+
bool CheckExpressionNodeForPushdown(const TExprBase& node, const TExprNode* lambdaArg, const TPushdownOptions& options) {
181+
if (options.AllowOlapApply) {
164182
if (node.Maybe<TCoJust>() || node.Maybe<TCoCoalesce>()) {
165183
return true;
166184
}
@@ -174,9 +192,9 @@ bool CheckExpressionNodeForPushdown(const TExprBase& node, const TExprNode* lamb
174192
}
175193

176194
if (const auto maybeSafeCast = node.Maybe<TCoSafeCast>()) {
177-
return IsSupportedCast(maybeSafeCast.Cast(), allowOlapApply);
195+
return IsSupportedCast(maybeSafeCast.Cast(), options.AllowOlapApply);
178196
} else if (const auto maybeData = node.Maybe<TCoDataCtor>()) {
179-
return IsSupportedDataType(maybeData.Cast(), allowOlapApply);
197+
return IsSupportedDataType(maybeData.Cast(), options.AllowOlapApply);
180198
} else if (const auto maybeMember = node.Maybe<TCoMember>()) {
181199
return IsMemberColumn(maybeMember.Cast(), lambdaArg);
182200
} else if (const auto maybeJsonValue = node.Maybe<TCoJsonValue>()) {
@@ -187,23 +205,23 @@ bool CheckExpressionNodeForPushdown(const TExprBase& node, const TExprNode* lamb
187205
}
188206

189207
if (const auto op = node.Maybe<TCoUnaryArithmetic>()) {
190-
return CheckExpressionNodeForPushdown(op.Cast().Arg(), lambdaArg, allowOlapApply) && IsGoodTypeForArithmeticPushdown(*op.Cast().Ref().GetTypeAnn(), allowOlapApply);
208+
return CheckExpressionNodeForPushdown(op.Cast().Arg(), lambdaArg, options) && IsGoodTypeForArithmeticPushdown(*op.Cast().Ref().GetTypeAnn(), options.AllowOlapApply);
191209
} else if (const auto op = node.Maybe<TCoBinaryArithmetic>()) {
192-
return CheckExpressionNodeForPushdown(op.Cast().Left(), lambdaArg, allowOlapApply) && CheckExpressionNodeForPushdown(op.Cast().Right(), lambdaArg, allowOlapApply)
193-
&& IsGoodTypeForArithmeticPushdown(*op.Cast().Ref().GetTypeAnn(), allowOlapApply) && !op.Cast().Maybe<TCoAggrAdd>();
210+
return CheckExpressionNodeForPushdown(op.Cast().Left(), lambdaArg, options) && CheckExpressionNodeForPushdown(op.Cast().Right(), lambdaArg, options)
211+
&& IsGoodTypeForArithmeticPushdown(*op.Cast().Ref().GetTypeAnn(), options.AllowOlapApply) && !op.Cast().Maybe<TCoAggrAdd>();
194212
}
195213

196-
if (allowOlapApply) {
214+
if (options.AllowOlapApply) {
197215
if (const auto maybeIfPresent = node.Maybe<TCoIfPresent>()) {
198-
return IfPresentCanBePushed(maybeIfPresent.Cast(), lambdaArg, allowOlapApply);
216+
return IfPresentCanBePushed(maybeIfPresent.Cast(), lambdaArg, options.AllowOlapApply);
199217
}
200-
return AbstractTreeCanBePushed(node, lambdaArg);
218+
return AbstractTreeCanBePushed(node, lambdaArg, options.PushdownSubstring);
201219
}
202220

203221
return false;
204222
}
205223

206-
bool IsGoodTypesForPushdownCompare(const TTypeAnnotationNode& typeOne, const TTypeAnnotationNode& typeTwo, bool allowOlapApply) {
224+
bool IsGoodTypesForPushdownCompare(const TTypeAnnotationNode& typeOne, const TTypeAnnotationNode& typeTwo, const TPushdownOptions& options) {
207225
const auto& rawOne = RemoveOptionality(typeOne);
208226
const auto& rawTwo = RemoveOptionality(typeTwo);
209227
if (IsSameAnnotation(rawOne, rawTwo))
@@ -225,22 +243,22 @@ bool IsGoodTypesForPushdownCompare(const TTypeAnnotationNode& typeOne, const TTy
225243
if (size != itemsTwo.size())
226244
return false;
227245
for (auto i = 0U; i < size; ++i) {
228-
if (!IsGoodTypesForPushdownCompare(*itemsOne[i], *itemsTwo[i], allowOlapApply)) {
246+
if (!IsGoodTypesForPushdownCompare(*itemsOne[i], *itemsTwo[i], options)) {
229247
return false;
230248
}
231249
}
232250
return true;
233251
}
234252
case ETypeAnnotationKind::Data: {
235-
return IsGoodTypeForComparsionPushdown(typeOne, allowOlapApply) && IsGoodTypeForComparsionPushdown(typeTwo, allowOlapApply);
253+
return IsGoodTypeForComparsionPushdown(typeOne, options.AllowOlapApply) && IsGoodTypeForComparsionPushdown(typeTwo, options.AllowOlapApply);
236254
}
237255
default:
238256
break;
239257
}
240258
return false;
241259
}
242260

243-
bool CheckComparisonParametersForPushdown(const TCoCompare& compare, const TExprNode* lambdaArg, const TExprBase& input, bool allowOlapApply) {
261+
bool CheckComparisonParametersForPushdown(const TCoCompare& compare, const TExprNode* lambdaArg, const TExprBase& input, const TPushdownOptions& options) {
244262
const auto* inputType = input.Ref().GetTypeAnn();
245263
switch (inputType->GetKind()) {
246264
case ETypeAnnotationKind::Flow:
@@ -263,7 +281,7 @@ bool CheckComparisonParametersForPushdown(const TCoCompare& compare, const TExpr
263281
return false;
264282
}
265283

266-
if (!IsGoodTypesForPushdownCompare(*compare.Left().Ref().GetTypeAnn(), *compare.Right().Ref().GetTypeAnn(), allowOlapApply)) {
284+
if (!IsGoodTypesForPushdownCompare(*compare.Left().Ref().GetTypeAnn(), *compare.Right().Ref().GetTypeAnn(), options)) {
267285
return false;
268286
}
269287

@@ -272,19 +290,19 @@ bool CheckComparisonParametersForPushdown(const TCoCompare& compare, const TExpr
272290
YQL_ENSURE(leftList.size() == rightList.size(), "Different sizes of lists in comparison!");
273291

274292
for (size_t i = 0; i < leftList.size(); ++i) {
275-
if (!CheckExpressionNodeForPushdown(leftList[i], lambdaArg, allowOlapApply) || !CheckExpressionNodeForPushdown(rightList[i], lambdaArg, allowOlapApply)) {
293+
if (!CheckExpressionNodeForPushdown(leftList[i], lambdaArg, options) || !CheckExpressionNodeForPushdown(rightList[i], lambdaArg, options)) {
276294
return false;
277295
}
278296
}
279297

280298
return true;
281299
}
282300

283-
bool CompareCanBePushed(const TCoCompare& compare, const TExprNode* lambdaArg, const TExprBase& lambdaBody, bool allowOlapApply) {
284-
return IsSupportedPredicate(compare) && CheckComparisonParametersForPushdown(compare, lambdaArg, lambdaBody, allowOlapApply);
301+
bool CompareCanBePushed(const TCoCompare& compare, const TExprNode* lambdaArg, const TExprBase& lambdaBody, const TPushdownOptions& options) {
302+
return IsSupportedPredicate(compare) && CheckComparisonParametersForPushdown(compare, lambdaArg, lambdaBody, options);
285303
}
286304

287-
bool SafeCastCanBePushed(const TCoFlatMap& flatmap, const TExprNode* lambdaArg, bool allowOlapApply) {
305+
bool SafeCastCanBePushed(const TCoFlatMap& flatmap, const TExprNode* lambdaArg, const TPushdownOptions& options) {
288306
/*
289307
* There are three ways of comparison in following format:
290308
*
@@ -305,7 +323,7 @@ bool SafeCastCanBePushed(const TCoFlatMap& flatmap, const TExprNode* lambdaArg,
305323
YQL_ENSURE(leftList.size() == rightList.size(), "Different sizes of lists in comparison!");
306324

307325
for (size_t i = 0; i < leftList.size(); ++i) {
308-
if (!CheckExpressionNodeForPushdown(leftList[i], lambdaArg, allowOlapApply) || !CheckExpressionNodeForPushdown(rightList[i], lambdaArg, allowOlapApply)) {
326+
if (!CheckExpressionNodeForPushdown(leftList[i], lambdaArg, options) || !CheckExpressionNodeForPushdown(rightList[i], lambdaArg, options)) {
309327
return false;
310328
}
311329
}
@@ -339,20 +357,20 @@ bool JsonExistsCanBePushed(const TCoJsonExists& jsonExists, const TExprNode* lam
339357
return true;
340358
}
341359

342-
bool CoalesceCanBePushed(const TCoCoalesce& coalesce, const TExprNode* lambdaArg, const TExprBase& lambdaBody, bool allowOlapApply) {
360+
bool CoalesceCanBePushed(const TCoCoalesce& coalesce, const TExprNode* lambdaArg, const TExprBase& lambdaBody, const TPushdownOptions& options) {
343361
if (!coalesce.Value().Maybe<TCoBool>()) {
344362
return false;
345363
}
346364

347365
const auto predicate = coalesce.Predicate();
348366
if (const auto maybeCompare = predicate.Maybe<TCoCompare>()) {
349-
return CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, allowOlapApply);
367+
return CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, options);
350368
} else if (const auto maybeFlatmap = predicate.Maybe<TCoFlatMap>()) {
351-
return SafeCastCanBePushed(maybeFlatmap.Cast(), lambdaArg, allowOlapApply);
369+
return SafeCastCanBePushed(maybeFlatmap.Cast(), lambdaArg, options);
352370
} else if (const auto maybeJsonExists = predicate.Maybe<TCoJsonExists>()) {
353371
return JsonExistsCanBePushed(maybeJsonExists.Cast(), lambdaArg);
354372
} else if (const auto maybeIfPresent = predicate.Maybe<TCoIfPresent>()) {
355-
return IfPresentCanBePushed(maybeIfPresent.Cast(), lambdaArg, allowOlapApply);
373+
return IfPresentCanBePushed(maybeIfPresent.Cast(), lambdaArg, options.AllowOlapApply);
356374
}
357375

358376
return false;
@@ -362,7 +380,7 @@ bool ExistsCanBePushed(const TCoExists& exists, const TExprNode* lambdaArg) {
362380
return IsMemberColumn(exists.Optional(), lambdaArg);
363381
}
364382

365-
void CollectChildrenPredicates(const TExprNode& opNode, TOLAPPredicateNode& predicateTree, const TExprNode* lambdaArg, const TExprBase& lambdaBody, bool allowOlapApply) {
383+
void CollectChildrenPredicates(const TExprNode& opNode, TOLAPPredicateNode& predicateTree, const TExprNode* lambdaArg, const TExprBase& lambdaBody, const TPushdownOptions& options) {
366384
predicateTree.Children.reserve(opNode.ChildrenSize());
367385
predicateTree.CanBePushed = true;
368386
predicateTree.CanBePushedApply = true;
@@ -374,8 +392,9 @@ void CollectChildrenPredicates(const TExprNode& opNode, TOLAPPredicateNode& pred
374392
child.CanBePushed = IsSupportedDataType(maybeCtor.Cast(), false);
375393
child.CanBePushedApply = IsSupportedDataType(maybeCtor.Cast(), true);
376394
}
377-
else
378-
CollectPredicates(TExprBase(child.ExprNode), child, lambdaArg, lambdaBody, allowOlapApply);
395+
else {
396+
CollectPredicates(TExprBase(child.ExprNode), child, lambdaArg, lambdaBody, options);
397+
}
379398
predicateTree.Children.emplace_back(child);
380399
predicateTree.CanBePushed &= child.CanBePushed;
381400
predicateTree.CanBePushedApply &= child.CanBePushedApply;
@@ -384,15 +403,15 @@ void CollectChildrenPredicates(const TExprNode& opNode, TOLAPPredicateNode& pred
384403

385404
} // namespace
386405

387-
void CollectPredicates(const TExprBase& predicate, TOLAPPredicateNode& predicateTree, const TExprNode* lambdaArg, const TExprBase& lambdaBody, bool allowOlapApply) {
406+
void CollectPredicates(const TExprBase& predicate, TOLAPPredicateNode& predicateTree, const TExprNode* lambdaArg, const TExprBase& lambdaBody, const TPushdownOptions& options) {
388407
if (predicate.Maybe<TCoNot>() || predicate.Maybe<TCoAnd>() || predicate.Maybe<TCoOr>() || predicate.Maybe<TCoXor>()) {
389-
CollectChildrenPredicates(predicate.Ref(), predicateTree, lambdaArg, lambdaBody, allowOlapApply);
408+
CollectChildrenPredicates(predicate.Ref(), predicateTree, lambdaArg, lambdaBody, options);
390409
} else if (const auto maybeCoalesce = predicate.Maybe<TCoCoalesce>()) {
391-
predicateTree.CanBePushed = CoalesceCanBePushed(maybeCoalesce.Cast(), lambdaArg, lambdaBody, false);
392-
predicateTree.CanBePushedApply = CoalesceCanBePushed(maybeCoalesce.Cast(), lambdaArg, lambdaBody, true);
410+
predicateTree.CanBePushed = CoalesceCanBePushed(maybeCoalesce.Cast(), lambdaArg, lambdaBody, {false, options.PushdownSubstring});
411+
predicateTree.CanBePushedApply = CoalesceCanBePushed(maybeCoalesce.Cast(), lambdaArg, lambdaBody, {true, options.PushdownSubstring});
393412
} else if (const auto maybeCompare = predicate.Maybe<TCoCompare>()) {
394-
predicateTree.CanBePushed = CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, false);
395-
predicateTree.CanBePushedApply = CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, true);
413+
predicateTree.CanBePushed = CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, {false, options.PushdownSubstring});
414+
predicateTree.CanBePushedApply = CompareCanBePushed(maybeCompare.Cast(), lambdaArg, lambdaBody, {false, options.PushdownSubstring});
396415
} else if (const auto maybeExists = predicate.Maybe<TCoExists>()) {
397416
predicateTree.CanBePushed = ExistsCanBePushed(maybeExists.Cast(), lambdaArg);
398417
predicateTree.CanBePushedApply = predicateTree.CanBePushed;
@@ -401,12 +420,12 @@ void CollectPredicates(const TExprBase& predicate, TOLAPPredicateNode& predicate
401420
predicateTree.CanBePushedApply = predicateTree.CanBePushed;
402421
}
403422

404-
if (allowOlapApply && !predicateTree.CanBePushedApply){
423+
if (options.AllowOlapApply && !predicateTree.CanBePushedApply){
405424
if (predicate.Maybe<TCoIf>() || predicate.Maybe<TCoJust>() || predicate.Maybe<TCoCoalesce>()) {
406-
CollectChildrenPredicates(predicate.Ref(), predicateTree, lambdaArg, lambdaBody, true);
425+
CollectChildrenPredicates(predicate.Ref(), predicateTree, lambdaArg, lambdaBody, {true, options.PushdownSubstring});
407426
}
408427
if (!predicateTree.CanBePushedApply) {
409-
predicateTree.CanBePushedApply = AbstractTreeCanBePushed(predicate, lambdaArg);
428+
predicateTree.CanBePushedApply = AbstractTreeCanBePushed(predicate, lambdaArg, options.PushdownSubstring);
410429
}
411430
}
412431
}

0 commit comments

Comments
 (0)