From aee88f0731b109c95fb514344a9a4f1a28c2bdde Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Thu, 10 Oct 2024 17:29:46 -0700 Subject: [PATCH 01/11] init --- .../Microsoft.PowerFx.Core/Binding/Binder.cs | 14 ++++- .../External/IExternalNamedFormula.cs | 4 +- .../Functions/UserDefinedFunction.cs | 2 + .../Localization/Strings.cs | 2 + .../Public/DefinitionsCheckResult.cs | 2 +- src/strings/PowerFxResources.en-US.resx | 54 +++++++++++++++++++ .../RecalcEngineTests.cs | 37 +++++++++++++ 7 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs b/src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs index 7b6bf28cce..fec4603499 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs @@ -2940,7 +2940,13 @@ public override void Visit(FirstNameNode node) _txb.SetSetMutable(node, nameSymbol?.Props.CanSetMutate ?? false); if (lookupInfo.Data is IExternalNamedFormula formula) { - isConstantNamedFormula = formula.IsConstant; + isConstantNamedFormula = formula.IsConstant; + + // If the definition of the named formula has a delegation warning, every use should also inherit this warning + if (formula.HasDelegationWarning) + { + _txb.ErrorContainer.EnsureError(DocumentErrorSeverity.Warning, node, TexlStrings.SuggestRemoteExecutionHint_NF, node.Ident.Name); + } } } else if (lookupInfo.Kind == BindKind.Data) @@ -4872,6 +4878,12 @@ private void FinalizeCall(CallNode node) { _txb.ErrorContainer.EnsureError(node, errorKey, badAncestor.Head.Name); } + } + + // If the definition of the user-defined function has a delegation warning, every usage should also inherit this warning + if (func is UserDefinedFunction udf && udf.HasDelegationWarning) + { + _txb.ErrorContainer.EnsureError(DocumentErrorSeverity.Warning, node, TexlStrings.SuggestRemoteExecutionHint_UDF, udf.Name); } _txb.CheckAndMarkAsDelegatable(node); diff --git a/src/libraries/Microsoft.PowerFx.Core/Entities/External/IExternalNamedFormula.cs b/src/libraries/Microsoft.PowerFx.Core/Entities/External/IExternalNamedFormula.cs index a35cf95ebb..880b0f44ee 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Entities/External/IExternalNamedFormula.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Entities/External/IExternalNamedFormula.cs @@ -13,6 +13,8 @@ internal interface IExternalNamedFormula : IExternalPageableSymbol, IExternalDel bool IsConstant { get; } - bool ContainsReferenceToView { get; } + bool ContainsReferenceToView { get; } + + bool HasDelegationWarning { get; } } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs index 662bf0eeb9..f86086b4e6 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs @@ -75,6 +75,8 @@ public override bool TryGetDataSource(CallNode callNode, TexlBinding binding, ou return TryGetExternalDataSource(out dsInfo); } + public bool HasDelegationWarning => _binding?.ErrorContainer.GetErrors().Any(error => error.MessageKey.Contains("SuggestRemoteExecutionHint")) ?? false; + /// /// Initializes a new instance of the class. /// diff --git a/src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs b/src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs index 66ed74e2d9..4b399d6444 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs @@ -758,6 +758,8 @@ internal static class TexlStrings public static ErrorResourceKey SuggestRemoteExecutionHint_InOpRhs = new ErrorResourceKey("SuggestRemoteExecutionHint_InOpRhs"); public static ErrorResourceKey SuggestRemoteExecutionHint_StringMatchSecondParam = new ErrorResourceKey("SuggestRemoteExecutionHint_StringMatchSecondParam"); public static ErrorResourceKey SuggestRemoteExecutionHint_InOpInvalidColumn = new ErrorResourceKey("SuggestRemoteExecutionHint_InOpInvalidColumn"); + public static ErrorResourceKey SuggestRemoteExecutionHint_NF = new ErrorResourceKey("SuggestRemoteExecutionHint_NF"); + public static ErrorResourceKey SuggestRemoteExecutionHint_UDF = new ErrorResourceKey("SuggestRemoteExecutionHint_UDF"); public static ErrorResourceKey OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn = new ErrorResourceKey("SuggestRemoteExecutionHint_OpNotSupportedByColumn"); public static ErrorResourceKey OpNotSupportedByServiceSuggestionMessage_OpNotSupportedByService = new ErrorResourceKey("SuggestRemoteExecutionHint_OpNotSupportedByService"); public static ErrorResourceKey OpNotSupportedByClientSuggestionMessage_OpNotSupportedByClient = new ErrorResourceKey("SuggestRemoteExecutionHint_OpNotSupportedByClient"); diff --git a/src/libraries/Microsoft.PowerFx.Core/Public/DefinitionsCheckResult.cs b/src/libraries/Microsoft.PowerFx.Core/Public/DefinitionsCheckResult.cs index 51cc3a5ad5..7b708f3c31 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Public/DefinitionsCheckResult.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Public/DefinitionsCheckResult.cs @@ -173,7 +173,7 @@ internal TexlFunctionSet ApplyCreateUserDefinedFunctions() List bindErrors = new List(); - if (binding.ErrorContainer.HasErrors()) + if (binding.ErrorContainer.GetErrors().Any(error => error.Severity > DocumentErrorSeverity.Warning)) { _errors.AddRange(ExpressionError.New(binding.ErrorContainer.GetErrors(), _defaultErrorCulture)); } diff --git a/src/strings/PowerFxResources.en-US.resx b/src/strings/PowerFxResources.en-US.resx index d51308d23d..81524e670d 100644 --- a/src/strings/PowerFxResources.en-US.resx +++ b/src/strings/PowerFxResources.en-US.resx @@ -4026,6 +4026,60 @@ https://go.microsoft.com/fwlink/?linkid=2132702 {Locked} + + + Delegation warning. The named formula {0} is not delegatable so its usage in this formula might not work correctly on large data sets. + Error Message. + + + The data source might not be able to process the formula and might return an incomplete data set. Your application might not return correct results or behave correctly if the data set is incomplete. + + + Try simplifying the definition for the user-defined function. + 1 How to fix the error. + + + Article: Understand delegation in a canvas app + Article on delegation + + + https://go.microsoft.com/fwlink/?linkid=2132701 + {Locked} + + + Blog: Data row limits for delegation + Blog: Data row limits for delegation + + + https://go.microsoft.com/fwlink/?linkid=2132702 + {Locked} + + + Delegation warning. The user-defined function {0} is not delegatable so its usage in this formula might not work correctly on large data sets. + Error Message. + + + The data source might not be able to process the formula and might return an incomplete data set. Your application might not return correct results or behave correctly if the data set is incomplete. + + + Try simplifying the definition for the user-defined function. + 1 How to fix the error. + + + Article: Understand delegation in a canvas app + Article on delegation + + + https://go.microsoft.com/fwlink/?linkid=2132701 + {Locked} + + + Blog: Data row limits for delegation + Blog: Data row limits for delegation + + + https://go.microsoft.com/fwlink/?linkid=2132702 + {Locked} Delegation warning. The highlighted part of this formula might not work correctly on large data sets. The "{0}" operation is not supported by this connector. diff --git a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs index 831d36f687..9f9a3b16b5 100644 --- a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs +++ b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs @@ -798,6 +798,43 @@ public void DelegableUDFTest() // Binding fails for recursive definitions and hence function is not added. Assert.False(recalcEngine.AddUserDefinedFunction("E():Void = { E(); };", CultureInfo.InvariantCulture, symbolTable: recalcEngine.EngineSymbols, allowSideEffects: true).IsSuccess); + } + + [Fact] + public void TestInheritanceOfDelegationWarningsInUDFs() + { + var symbolTable = new DelegatableSymbolTable(); + var schema = DType.CreateTable( + new TypedName(DType.Number, new DName("Value"))); + symbolTable.AddEntity(new TestDelegableDataSource( + "MyDataSource", + schema, + new TestDelegationMetadata( + new DelegationCapability(DelegationCapability.Filter), + schema, + new FilterOpMetadata( + schema, + new Dictionary(), + new Dictionary(), + new DelegationCapability(DelegationCapability.GreaterThan), + null)), + true)); + symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); + var config = new PowerFxConfig() + { + SymbolTable = symbolTable + }; + var engine = new RecalcEngine(config); + + var result = engine.AddUserDefinedFunction("NonDelegatableUDF():MyDataSourceTableType = Filter(MyDataSource, Sqrt(Value) > 5);", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + var func = engine.Functions.WithName("NonDelegatableUDF").First() as UserDefinedFunction; + Assert.True(func.HasDelegationWarning); + + result = engine.AddUserDefinedFunction("NonDelegatableUDF2():MyDataSourceTableType = NonDelegatableUDF();", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; + Assert.True(func.HasDelegationWarning); } // Binding to inner functions does not impact outer functions. From ea754c8a887baaef16d75830325974c385ac652b Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Fri, 11 Oct 2024 13:35:49 -0700 Subject: [PATCH 02/11] feedback --- src/strings/PowerFxResources.en-US.resx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/strings/PowerFxResources.en-US.resx b/src/strings/PowerFxResources.en-US.resx index 81524e670d..a745ad5abb 100644 --- a/src/strings/PowerFxResources.en-US.resx +++ b/src/strings/PowerFxResources.en-US.resx @@ -4028,14 +4028,14 @@ {Locked} - Delegation warning. The named formula {0} is not delegatable so its usage in this formula might not work correctly on large data sets. + Delegation warning. The named formula {0} is not delegable so its usage in this formula might not work correctly on large data sets. Error Message. The data source might not be able to process the formula and might return an incomplete data set. Your application might not return correct results or behave correctly if the data set is incomplete. - Try simplifying the definition for the user-defined function. + Try simplifying the definition for the named formula. 1 How to fix the error. @@ -4055,7 +4055,7 @@ {Locked} - Delegation warning. The user-defined function {0} is not delegatable so its usage in this formula might not work correctly on large data sets. + Delegation warning. The user-defined function {0} is not delegable so its usage in this formula might not work correctly on large data sets. Error Message. From 33cd82c15f7fbe3951945693eda3be9c90c478d8 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Wed, 16 Oct 2024 08:07:41 -0700 Subject: [PATCH 03/11] init --- .../DelegationValidationStrategy.cs | 39 ++++++---- .../IOpDelegationStrategy.cs | 2 +- .../InOpDelegationStrategy.cs | 4 +- .../OpDelegationStrategy.cs | 16 ++-- .../UnaryOpDelegationStrategy.cs | 8 +- .../Functions/TexlFunction.cs | 4 +- .../Functions/UserDefinedFunction.cs | 24 ++++-- .../Texl/Builtins/AsType.cs | 2 +- .../Texl/Builtins/DateTime.cs | 4 +- .../Texl/Builtins/EndsWith.cs | 4 +- .../Texl/Builtins/FilterDelegationBase.cs | 8 +- .../Texl/Builtins/IsBlank.cs | 6 +- .../Texl/Builtins/Len.cs | 4 +- .../Texl/Builtins/Logical.cs | 12 +-- .../Texl/Builtins/Lookup.cs | 4 +- .../Texl/Builtins/LowerUpper.cs | 2 +- .../Texl/Builtins/NotFunction.cs | 12 +-- .../Texl/Builtins/Proper.cs | 2 +- .../Texl/Builtins/StartsWith.cs | 4 +- .../Texl/Builtins/StringOneArgFunction.cs | 8 +- .../Texl/Builtins/StringTwoArgFunction.cs | 8 +- .../Texl/Builtins/Trim.cs | 4 +- .../Helpers/TestTabularDataSource.cs | 2 +- .../UserDefinedFunctionTests.cs | 20 +++++ .../RecalcEngineTests.cs | 74 +++++++++++++++++++ 25 files changed, 197 insertions(+), 80 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index 651d818197..ff1b6cdbb8 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using System.Collections.Generic; using System.Globalization; using System.Security.Authentication.ExtendedProtection; using Microsoft.PowerFx.Core.Binding; using Microsoft.PowerFx.Core.Binding.BindInfo; using Microsoft.PowerFx.Core.Entities; using Microsoft.PowerFx.Core.Errors; +using Microsoft.PowerFx.Core.Functions.Delegation.DelegationMetadata; using Microsoft.PowerFx.Core.Localization; using Microsoft.PowerFx.Core.Logging.Trackers; using Microsoft.PowerFx.Core.Texl.Builtins; @@ -18,17 +20,17 @@ namespace Microsoft.PowerFx.Core.Functions.Delegation.DelegationStrategies { internal interface ICallNodeDelegatableNodeValidationStrategy { - bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null); + bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false); } internal interface IDottedNameNodeDelegatableNodeValidationStrategy { - bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy); + bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false); } internal interface IFirstNameNodeDelegatableNodeValidationStrategy { - bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy); + bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false); } internal class DelegationValidationStrategy @@ -175,13 +177,13 @@ private OperationCapabilityMetadata GetScopedOperationCapabilityMetadata(IDelega return delegationMetadata.FilterDelegationMetadata; } - public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy) + public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(binding); Contracts.AssertValueOrNull(opDelStrategy); - var isRowScoped = binding.IsRowScope(node); + var isRowScoped = binding.IsRowScope(node) || nodeInheritsRowScope; if (!isRowScoped) { return IsValidNonRowScopedDottedNameNode(node, binding, metadata, opDelStrategy); @@ -262,13 +264,13 @@ public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, Oper return opDelStrategy?.IsOpSupportedByColumn(entityCapabilityMetadata, node, entityColumnPath, binding) ?? true; } - public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy) + public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(binding); Contracts.AssertValueOrNull(opDelStrategy); - var isRowScoped = binding.IsRowScope(node); + var isRowScoped = binding.IsRowScope(node) || nodeInheritsRowScope; var isValid = IsValidAsyncOrImpureNode(node, binding); if (isValid && !isRowScoped) { @@ -312,12 +314,12 @@ private IDelegationMetadata GetCapabilityMetadata(FirstNameInfo info) } // Verifies if provided column node supports delegation. - protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, DelegationCapability capability) + protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, DelegationCapability capability, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(binding); Contracts.AssertValueOrNull(opDelStrategy); - Contracts.Assert(binding.IsRowScope(node)); + Contracts.Assert(binding.IsRowScope(node) || nodeInheritsRowScope); var firstNameInfo = binding.GetInfo(node.AsFirstName()); if (firstNameInfo == null) @@ -359,7 +361,7 @@ protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, return true; } - public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null) + public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) { // Functions may have their specific CallNodeDelegationStrategies (i.e. AsType, User) // so, if available, we need to ensure we use their specific delegation strategy. @@ -370,13 +372,13 @@ public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, Operatio if (function != null && function != Function) { // We need to keep track of the tracking function for delegation tracking telemetry to be consistent. - return function.GetCallNodeDelegationStrategy().IsValidCallNode(node, binding, metadata, trackingFunction ?? Function); + return function.GetCallNodeDelegationStrategy().IsValidCallNode(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); } - return IsValidCallNodeInternal(node, binding, metadata, trackingFunction ?? Function); + return IsValidCallNodeInternal(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); } - protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null) + protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -393,7 +395,7 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera } // If the node is not row scoped and it's valid then it can be delegated. - var isRowScoped = binding.IsRowScope(node); + var isRowScoped = binding.IsRowScope(node) || nodeInheritsRowScope; if (!isRowScoped) { return true; @@ -405,9 +407,16 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera SuggestDelegationHint(node, binding, warning, new object[] { callInfo?.Function.Name }); } - if (callInfo?.Function != null && ((TexlFunction)callInfo.Function).IsRowScopedServerDelegatable(node, binding, metadata)) + if (callInfo?.Function != null && ((TexlFunction)callInfo.Function).IsRowScopedServerDelegatable(node, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope)) { return true; + } + + if (callInfo?.Function is UserDefinedFunction udf && + ((FilterFunction)trackingFunction).IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, nodeInheritsRowScope: isRowScoped)) + { + udf.SetSupportRowScopedServerDelegation(true); + return true; } var telemetryMessage = string.Format(CultureInfo.InvariantCulture, "Kind:{0}, isRowScoped:{1}", node.Kind, isRowScoped); diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/IOpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/IOpDelegationStrategy.cs index 41bfc2a4d3..514b511fc8 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/IOpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/IOpDelegationStrategy.cs @@ -13,6 +13,6 @@ internal interface IOpDelegationStrategy bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, TexlNode node, TexlBinding binder); - bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding); + bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false); } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs index 1152512a09..9759e43e33 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs @@ -25,7 +25,7 @@ public InOpDelegationStrategy(BinaryOpNode node, TexlFunction function) _binaryOpNode = node; } - public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding) + public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -52,7 +52,7 @@ public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadat return false; } - var isRowScopedOrLambda = IsRowScopedOrLambda(binding, node, info, columnName, metadata); + var isRowScopedOrLambda = IsRowScopedOrLambda(binding, node, info, columnName, metadata) || nodeInheritsRowScope; if (!isRowScopedOrLambda) { return false; diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs index 2f5098d18a..265e0c4793 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs @@ -80,7 +80,7 @@ public virtual bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, T } // Verifies if given kind of node is supported by function delegation. - private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode) + private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -112,7 +112,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } // Non row-scope, non async, pure nodes should always be valid because we can calculate value in runtime before delegation. - if (!binding.IsRowScope(node) && !binding.IsAsync(node) && binding.IsPure(node)) + if (!binding.IsRowScope(node) && !binding.IsAsync(node) && binding.IsPure(node) && !nodeInheritsRowScope) { return true; } @@ -138,7 +138,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); - return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata); + return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.FirstName: @@ -156,7 +156,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata var unaryopNode = node.AsUnaryOpLit(); var unaryOpNodeDelegationStrategy = _function.GetOpDelegationStrategy(unaryopNode.Op); - return unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding); + return unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.BinaryOp: @@ -172,7 +172,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata var binaryOpDelStrategy = (opDelStrategy as BinaryOpDelegationStrategy).VerifyValue(); Contracts.Assert(binaryOpNode.Op == binaryOpDelStrategy.Op); - if (!opDelStrategy.IsSupportedOpNode(node, metadata, binding)) + if (!opDelStrategy.IsSupportedOpNode(node, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope)) { SuggestDelegationHint(binaryOpNode, binding); return false; @@ -263,7 +263,7 @@ private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetad return true; } - public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding) + public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -298,13 +298,13 @@ public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata return false; } - if (!IsSupportedNode(binaryOpNode.Left, metadata, binding, opDelStrategy, false)) + if (!IsSupportedNode(binaryOpNode.Left, metadata, binding, opDelStrategy, false, nodeInheritsRowScope)) { SuggestDelegationHint(node, binding); return false; } - if (!IsSupportedNode(binaryOpNode.Right, metadata, binding, opDelStrategy, true)) + if (!IsSupportedNode(binaryOpNode.Right, metadata, binding, opDelStrategy, true, nodeInheritsRowScope)) { SuggestDelegationHint(node, binding); return false; diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs index 38a04a711d..446f5a6272 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs @@ -69,14 +69,14 @@ public virtual bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, T return true; } - private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy) + private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValue(opDelStrategy); - if (!binding.IsRowScope(node)) + if (!binding.IsRowScope(node) && nodeInheritsRowScope) { return true; } @@ -150,7 +150,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata return false; } - public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding) + public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -179,7 +179,7 @@ public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata return false; } - return IsSupportedNode(unaryOpNode.Child, metadata, binding, opDelStrategy); + return IsSupportedNode(unaryOpNode.Child, metadata, binding, opDelStrategy, nodeInheritsRowScope); } } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs index 5271868816..d2a17e8dab 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs @@ -945,12 +945,12 @@ public virtual bool SupportsPaging(CallNode callNode, TexlBinding binding) // Returns true if function is row scoped and supports delegation. // Needs to be overriden by functions (For example, IsBlank) which are not server delegatable themselves but can become one when scoped inside a delegatable function. - public virtual bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public virtual bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); - if (!binding.IsRowScope(callNode)) + if (!binding.IsRowScope(callNode) && !nodeInheritsRowScope) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs index f86086b4e6..c4047ecf4f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs @@ -12,6 +12,7 @@ using Microsoft.PowerFx.Core.Binding.BindInfo; using Microsoft.PowerFx.Core.Entities; using Microsoft.PowerFx.Core.Errors; +using Microsoft.PowerFx.Core.Functions.Delegation; using Microsoft.PowerFx.Core.Functions.FunctionArgValidators; using Microsoft.PowerFx.Core.Glue; using Microsoft.PowerFx.Core.IR; @@ -44,7 +45,9 @@ internal class UserDefinedFunction : TexlFunction, IExternalPageableSymbol, IExt public bool IsPageable => _binding.IsPageable(_binding.Top); - public bool IsDelegatable => _binding.IsDelegatable(_binding.Top); + public bool IsDelegatable => _binding.IsDelegatable(_binding.Top); + + public bool SupportsRowScopedServerDelegation; public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) { @@ -53,7 +56,12 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) Contracts.Assert(binding.GetInfo(callNode).Function is UserDefinedFunction udf && udf.Binding != null); return base.IsServerDelegatable(callNode, binding) || IsDelegatable; - } + } + + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + { + return SupportsRowScopedServerDelegation; + } public override bool SupportsParamCoercion => true; @@ -92,7 +100,8 @@ public UserDefinedFunction(string functionName, DType returnType, TexlNode body, this._args = args; this._isImperative = isImperative; - this.UdfBody = body; + this.UdfBody = body; + this.SupportsRowScopedServerDelegation = false; } /// @@ -370,8 +379,13 @@ internal static bool IsRestrictedType(FormulaType ft) } return false; - } - + } + + internal void SetSupportRowScopedServerDelegation(bool value) + { + SupportsRowScopedServerDelegation = value; + } + /// /// NameResolver that combines global named resolver and params for user defined function. /// diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs index 38d9fc218b..bf4022c9ac 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs @@ -106,7 +106,7 @@ private static bool IsExternalSource(object externalDataSource) tDsInfo is IExternalTabularDataSource; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { return metadata.IsDelegationSupportedByTable(DelegationCapability.AsType); } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs index 56243875a4..c71e58c7db 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs @@ -53,13 +53,13 @@ public ExtractDateTimeFunctionBase(string name, TexlStrings.StringGetter descrip { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); Contracts.AssertValue(metadata); - return base.IsRowScopedServerDelegatable(callNode, binding, metadata); + return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs index acb665ab4b..b6ca0f7414 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs @@ -24,13 +24,13 @@ public EndsWithFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); Contracts.AssertValue(metadata); - return IsRowScopedServerDelegatableHelper(callNode, binding, metadata); + return IsRowScopedServerDelegatableHelper(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } public override IEnumerable GetSignatures() diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs index e78c7fcbd4..369ece953c 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs @@ -67,7 +67,7 @@ public override bool TryGetDelegationMetadata(CallNode node, TexlBinding binding // Determine whether a node can be delegated as part of a filter predicate. // The enforceBoolean flag determines whether to enforce the return type of the node. If the node is part of a filter predicate directly, it must return a boolean type. // If the node is used in other places inside a filter, such as in a nested LookUp reduction formula, it can return any type. - protected bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding binding, FilterOpMetadata filterMetadata, bool generateHints = true, bool enforceBoolean = true) + internal bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding binding, FilterOpMetadata filterMetadata, bool generateHints = true, bool enforceBoolean = true, bool nodeInheritsRowScope = false) { Contracts.AssertValue(dsNode); Contracts.AssertValue(binding); @@ -98,7 +98,7 @@ protected bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBindin var binaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op, opNode); Contracts.AssertValue(opNode); - if (!binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, filterMetadata, binding)) + if (!binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, filterMetadata, binding, nodeInheritsRowScope)) { return false; } @@ -144,7 +144,7 @@ protected bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBindin var unaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op); Contracts.AssertValue(opNode); - if (!unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, filterMetadata, binding)) + if (!unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, filterMetadata, binding, nodeInheritsRowScope)) { SuggestDelegationHint(dsNode, binding); return false; @@ -155,7 +155,7 @@ protected bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBindin case NodeKind.Call: { - if (!cNodeStrategy.IsValidCallNode(dsNode.AsCall(), binding, filterMetadata)) + if (!cNodeStrategy.IsValidCallNode(dsNode.AsCall(), binding, filterMetadata, nodeInheritsRowScope: nodeInheritsRowScope)) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs index 8b9e8842cc..e8c6949a6c 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs @@ -92,7 +92,7 @@ public IsBlankFunction() yield return new[] { TexlStrings.IsBlankArg1 }; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); @@ -113,7 +113,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding if (binding.IsFullRecordRowScopeAccess(args[0])) { - return GetDottedNameNodeDelegationStrategy().IsValidDottedNameNode(args[0] as DottedNameNode, binding, metadata, opStrategy); + return GetDottedNameNodeDelegationStrategy().IsValidDottedNameNode(args[0] as DottedNameNode, binding, metadata, opStrategy, nodeInheritsRowScope: nodeInheritsRowScope); } if (args[0] is not FirstNameNode node) @@ -129,7 +129,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding } var firstNameNodeValidationStrategy = GetFirstNameNodeDelegationStrategy(); - return firstNameNodeValidationStrategy.IsValidFirstNameNode(node, binding, opStrategy); + return firstNameNodeValidationStrategy.IsValidFirstNameNode(node, binding, opStrategy, nodeInheritsRowScope: nodeInheritsRowScope); } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs index 11a3b6935d..6876325039 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs @@ -31,9 +31,9 @@ public LenFunction() public override DelegationCapability FunctionDelegationCapability => DelegationCapability.Length; - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { - return base.IsRowScopedServerDelegatable(callNode, binding, metadata); + return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs index 7e15af17eb..ffea807151 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs @@ -85,7 +85,7 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp return fArgsValid; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); @@ -115,7 +115,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding case NodeKind.FirstName: { var firstNameStrategy = GetFirstNameNodeDelegationStrategy(); - if (!firstNameStrategy.IsValidFirstNameNode(arg.AsFirstName(), binding, null)) + if (!firstNameStrategy.IsValidFirstNameNode(arg.AsFirstName(), binding, null, nodeInheritsRowScope: nodeInheritsRowScope)) { return false; } @@ -126,7 +126,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding case NodeKind.Call: { var cNodeStrategy = GetCallNodeDelegationStrategy(); - if (!cNodeStrategy.IsValidCallNode(arg.AsCall(), binding, metadata)) + if (!cNodeStrategy.IsValidCallNode(arg.AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope)) { SuggestDelegationHint(arg, binding); return false; @@ -138,7 +138,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding case NodeKind.DottedName: { var dottedStrategy = GetDottedNameNodeDelegationStrategy(); - if (!dottedStrategy.IsValidDottedNameNode(arg.AsDottedName(), binding, metadata, null)) + if (!dottedStrategy.IsValidDottedNameNode(arg.AsDottedName(), binding, metadata, null, nodeInheritsRowScope: nodeInheritsRowScope)) { SuggestDelegationHint(arg, binding); return false; @@ -151,7 +151,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding { var opNode = arg.AsBinaryOp(); var binaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op, opNode); - if (!binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding)) + if (!binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope)) { SuggestDelegationHint(arg, binding); return false; @@ -164,7 +164,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding { var opNode = arg.AsUnaryOpLit(); var unaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op); - if (!unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding)) + if (!unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope)) { SuggestDelegationHint(arg, binding); return false; diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs index 2440d591c9..9347d0c040 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs @@ -161,7 +161,7 @@ public LookUpCallNodeDelegationStrategy(TexlFunction function) { } - public override bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null) + public override bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) { var function = binding.GetInfo(node)?.Function; var args = node.Args.Children.VerifyValue(); @@ -175,7 +175,7 @@ function is LookUpFunction lookup && args.Count > 2 && return false; } - return base.IsValidCallNode(node, binding, metadata, trackingFunction ?? Function); + return base.IsValidCallNode(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); } } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs index 41da68a9ee..bed2faae65 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs @@ -25,7 +25,7 @@ public LowerUpperFunction(bool isLower) public override DelegationCapability FunctionDelegationCapability => _isLower ? DelegationCapability.Lower : DelegationCapability.Upper; - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs index cce6287576..db876ae143 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs @@ -48,7 +48,7 @@ public override IOpDelegationStrategy GetOpDelegationStrategy(BinaryOp op, Power return new DefaultBinaryOpDelegationStrategy(op, BuiltinFunctionsCore.Filter); } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); @@ -72,7 +72,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding switch (argKind) { case NodeKind.FirstName: - return firstNameStrategy.IsValidFirstNameNode(args[0].AsFirstName(), binding, opStrategy); + return firstNameStrategy.IsValidFirstNameNode(args[0].AsFirstName(), binding, opStrategy, nodeInheritsRowScope: nodeInheritsRowScope); case NodeKind.Call: { @@ -81,7 +81,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding return false; } - return cNodeStrategy.IsValidCallNode(args[0].AsCall(), binding, metadata); + return cNodeStrategy.IsValidCallNode(args[0].AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.BinaryOp: @@ -93,7 +93,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding var opNode = args[0].AsBinaryOp(); var binaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op, opNode); - return binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding); + return binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.UnaryOp: @@ -105,11 +105,11 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding var opNode = args[0].AsUnaryOpLit(); var unaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op); - return unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding); + return unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.DottedName: - return dottedStrategy.IsValidDottedNameNode(args[0].AsDottedName(), binding, metadata, opStrategy); + return dottedStrategy.IsValidDottedNameNode(args[0].AsDottedName(), binding, metadata, opStrategy, nodeInheritsRowScope: nodeInheritsRowScope); default: return argKind == NodeKind.BoolLit; diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs index 8d49ff63e6..08c8e0b5a9 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs @@ -19,7 +19,7 @@ public ProperFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs index e3778ca453..55cc063484 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs @@ -21,13 +21,13 @@ public StartsWithFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); Contracts.AssertValue(metadata); - return IsRowScopedServerDelegatableHelper(callNode, binding, metadata); + return IsRowScopedServerDelegatableHelper(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } public override IEnumerable GetSignatures() diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs index 9a225e997d..2fba30085a 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs @@ -38,7 +38,7 @@ public StringOneArgFunction(string name, TexlStrings.StringGetter description, F yield return new[] { TexlStrings.StringFuncArg1 }; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); @@ -60,7 +60,7 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding case NodeKind.FirstName: { var firstNameStrategy = GetFirstNameNodeDelegationStrategy(); - return firstNameStrategy.IsValidFirstNameNode(args[0].AsFirstName(), binding, null); + return firstNameStrategy.IsValidFirstNameNode(args[0].AsFirstName(), binding, null, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.Call: @@ -71,13 +71,13 @@ public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding } var cNodeStrategy = GetCallNodeDelegationStrategy(); - return cNodeStrategy.IsValidCallNode(args[0].AsCall(), binding, metadata); + return cNodeStrategy.IsValidCallNode(args[0].AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } case NodeKind.DottedName: { var dottedStrategy = GetDottedNameNodeDelegationStrategy(); - return dottedStrategy.IsValidDottedNameNode(args[0].AsDottedName(), binding, metadata, null); + return dottedStrategy.IsValidDottedNameNode(args[0].AsDottedName(), binding, metadata, null, nodeInheritsRowScope: nodeInheritsRowScope); } default: diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringTwoArgFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringTwoArgFunction.cs index 39b9a1e974..d09c8f5206 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringTwoArgFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringTwoArgFunction.cs @@ -28,7 +28,7 @@ public StringTwoArgFunction(string name, TexlStrings.StringGetter description, D { } - protected bool IsRowScopedServerDelegatableHelper(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + protected bool IsRowScopedServerDelegatableHelper(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); @@ -57,7 +57,7 @@ protected bool IsRowScopedServerDelegatableHelper(CallNode callNode, TexlBinding { case NodeKind.FirstName: var firstNameStrategy = GetFirstNameNodeDelegationStrategy(); - if (!firstNameStrategy.IsValidFirstNameNode(arg.AsFirstName(), binding, null)) + if (!firstNameStrategy.IsValidFirstNameNode(arg.AsFirstName(), binding, null, nodeInheritsRowScope: nodeInheritsRowScope)) { return false; } @@ -70,7 +70,7 @@ protected bool IsRowScopedServerDelegatableHelper(CallNode callNode, TexlBinding } var cNodeStrategy = GetCallNodeDelegationStrategy(); - if (!cNodeStrategy.IsValidCallNode(arg.AsCall(), binding, metadata)) + if (!cNodeStrategy.IsValidCallNode(arg.AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope)) { return false; } @@ -81,7 +81,7 @@ protected bool IsRowScopedServerDelegatableHelper(CallNode callNode, TexlBinding case NodeKind.DottedName: { var dottedStrategy = GetDottedNameNodeDelegationStrategy(); - return dottedStrategy.IsValidDottedNameNode(arg.AsDottedName(), binding, metadata, null); + return dottedStrategy.IsValidDottedNameNode(arg.AsDottedName(), binding, metadata, null, nodeInheritsRowScope: nodeInheritsRowScope); } default: diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs index af2ca00c88..3daf028f01 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs @@ -38,13 +38,13 @@ public TrimEndsFunction() public override DelegationCapability FunctionDelegationCapability => DelegationCapability.Trim; - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); Contracts.AssertValue(metadata); - return base.IsRowScopedServerDelegatable(callNode, binding, metadata); + return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } } diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestTabularDataSource.cs b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestTabularDataSource.cs index 3baccab4f1..ff37c76800 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestTabularDataSource.cs +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestTabularDataSource.cs @@ -294,7 +294,7 @@ public bool TryGetRelatedColumn(string selectColumnName, out string additionalCo } } - internal class TestDelegableDataSource : TestDataSource + internal class TestDelegableDataSource : TestDataSource, IExternalDelegatableSymbol { private readonly TabularDataQueryOptions _queryOptions; private readonly IDelegationMetadata _delegationMetadata; diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs index 03dbc7e298..428c1b5ca5 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs @@ -652,6 +652,26 @@ public void TestUDFHasWarningWhenShadowing(string script) error.Severity == DocumentErrorSeverity.Warning)); } + [Theory] + [InlineData("MismatchType():Number = { 1+3; Set(x, 123); };")] + public void TestMismatchReturnType(string script) + { + var parserOptions = new ParserOptions() + { + AllowsSideEffects = true, + }; + var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library, FormulaType.PrimitiveTypes); + + var parseResult = UserDefinitions.Parse(script, parserOptions); + var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), nameResolver, out var errors); + errors.AddRange(parseResult.Errors ?? Enumerable.Empty()); + + // Only one error should exist. + Assert.True(errors.Count() == 1 && + errors.Any(error => error.MessageKey == "ErrUDF_ReturnTypeDoesNotMatch" && + error.Severity == DocumentErrorSeverity.Severe)); + } + [Fact] public void TestUDFRestrictedTypes() { diff --git a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs index 9f9a3b16b5..0ad38c6de4 100644 --- a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs +++ b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs @@ -835,6 +835,80 @@ public void TestInheritanceOfDelegationWarningsInUDFs() Assert.True(result.IsSuccess); func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; Assert.True(func.HasDelegationWarning); + } + + [Fact] + public void TestInheritanceOfDelegationWarningsInUDFs2() + { + var symbolTable = new DelegatableSymbolTable(); + var schema = DType.CreateTable( + new TypedName(DType.Number, new DName("Value"))); + symbolTable.AddEntity(new TestDelegableDataSource( + "MyDataSource", + schema, + new TestDelegationMetadata( + new DelegationCapability(DelegationCapability.Filter), + schema, + new FilterOpMetadata( + schema, + new Dictionary(), + new Dictionary(), + new DelegationCapability(DelegationCapability.GreaterThan), + null)), + true)); + symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); + var config = new PowerFxConfig() + { + SymbolTable = symbolTable + }; + var engine = new RecalcEngine(config); + + var result = engine.AddUserDefinedFunction("NonDelegatableUDF(Value: Number):Boolean = Value > 5;", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + var func = engine.Functions.WithName("NonDelegatableUDF").First() as UserDefinedFunction; + Assert.False(func.HasDelegationWarning); + + result = engine.AddUserDefinedFunction("NonDelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, NonDelegatableUDF(Value));", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; + Assert.True(!func.HasDelegationWarning); + } + + [Fact] + public void TestInheritanceOfDelegationWarningsInUDFs3() + { + var symbolTable = new DelegatableSymbolTable(); + var schema = DType.CreateTable( + new TypedName(DType.Number, new DName("Value"))); + symbolTable.AddEntity(new TestDelegableDataSource( + "MyDataSource", + schema, + new TestDelegationMetadata( + new DelegationCapability(DelegationCapability.Filter), + schema, + new FilterOpMetadata( + schema, + new Dictionary(), + new Dictionary(), + new DelegationCapability(DelegationCapability.GreaterThan), + null)), + true)); + symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); + var config = new PowerFxConfig() + { + SymbolTable = symbolTable + }; + var engine = new RecalcEngine(config); + + var result = engine.AddUserDefinedFunction("NonDelegatableUDF(Value: Number):Boolean = Sqrt(Value) > 5;", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + var func = engine.Functions.WithName("NonDelegatableUDF").First() as UserDefinedFunction; + Assert.False(func.HasDelegationWarning); + + result = engine.AddUserDefinedFunction("NonDelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, NonDelegatableUDF(Value));", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + Assert.True(result.IsSuccess); + func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; + Assert.True(func.HasDelegationWarning); } // Binding to inner functions does not impact outer functions. From faa5f4fe532b3dfc376fefa4e02b99f00474e5f9 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Mon, 21 Oct 2024 16:00:23 -0400 Subject: [PATCH 04/11] clean up --- .../UserDefinedFunctionTests.cs | 20 ------ .../RecalcEngineTests.cs | 68 ++++++------------- 2 files changed, 19 insertions(+), 69 deletions(-) diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs index 428c1b5ca5..03dbc7e298 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedFunctionTests.cs @@ -652,26 +652,6 @@ public void TestUDFHasWarningWhenShadowing(string script) error.Severity == DocumentErrorSeverity.Warning)); } - [Theory] - [InlineData("MismatchType():Number = { 1+3; Set(x, 123); };")] - public void TestMismatchReturnType(string script) - { - var parserOptions = new ParserOptions() - { - AllowsSideEffects = true, - }; - var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library, FormulaType.PrimitiveTypes); - - var parseResult = UserDefinitions.Parse(script, parserOptions); - var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), nameResolver, out var errors); - errors.AddRange(parseResult.Errors ?? Enumerable.Empty()); - - // Only one error should exist. - Assert.True(errors.Count() == 1 && - errors.Any(error => error.MessageKey == "ErrUDF_ReturnTypeDoesNotMatch" && - error.Severity == DocumentErrorSeverity.Severe)); - } - [Fact] public void TestUDFRestrictedTypes() { diff --git a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs index 0ad38c6de4..6c048cce39 100644 --- a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs +++ b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs @@ -837,12 +837,20 @@ public void TestInheritanceOfDelegationWarningsInUDFs() Assert.True(func.HasDelegationWarning); } - [Fact] - public void TestInheritanceOfDelegationWarningsInUDFs2() + [Theory] + [InlineData( + "DelegatableUDF(Value: Number):Boolean = Value > 5;", + "DelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, DelegatableUDF(Value));", + false)] + [InlineData( + "NonDelegatableUDF(Value: Number):Boolean = Sqrt(Value) > 5;", + "NonDelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, NonDelegatableUDF(Value));", + true)] + public void TestFilterFunctionDelegationUDF(string formula, string formula2, bool expectedError) { var symbolTable = new DelegatableSymbolTable(); var schema = DType.CreateTable( - new TypedName(DType.Number, new DName("Value"))); + new TypedName(DType.Number, new DName("Value"))); symbolTable.AddEntity(new TestDelegableDataSource( "MyDataSource", schema, @@ -856,59 +864,21 @@ public void TestInheritanceOfDelegationWarningsInUDFs2() new DelegationCapability(DelegationCapability.GreaterThan), null)), true)); - symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); + symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); var config = new PowerFxConfig() { - SymbolTable = symbolTable - }; + SymbolTable = symbolTable + }; var engine = new RecalcEngine(config); - - var result = engine.AddUserDefinedFunction("NonDelegatableUDF(Value: Number):Boolean = Value > 5;", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); - Assert.True(result.IsSuccess); - var func = engine.Functions.WithName("NonDelegatableUDF").First() as UserDefinedFunction; - Assert.False(func.HasDelegationWarning); - - result = engine.AddUserDefinedFunction("NonDelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, NonDelegatableUDF(Value));", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); - Assert.True(result.IsSuccess); - func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; - Assert.True(!func.HasDelegationWarning); - } - [Fact] - public void TestInheritanceOfDelegationWarningsInUDFs3() - { - var symbolTable = new DelegatableSymbolTable(); - var schema = DType.CreateTable( - new TypedName(DType.Number, new DName("Value"))); - symbolTable.AddEntity(new TestDelegableDataSource( - "MyDataSource", - schema, - new TestDelegationMetadata( - new DelegationCapability(DelegationCapability.Filter), - schema, - new FilterOpMetadata( - schema, - new Dictionary(), - new Dictionary(), - new DelegationCapability(DelegationCapability.GreaterThan), - null)), - true)); - symbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema)); - var config = new PowerFxConfig() - { - SymbolTable = symbolTable - }; - var engine = new RecalcEngine(config); - - var result = engine.AddUserDefinedFunction("NonDelegatableUDF(Value: Number):Boolean = Sqrt(Value) > 5;", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + var result = engine.AddUserDefinedFunction(formula, CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); Assert.True(result.IsSuccess); - var func = engine.Functions.WithName("NonDelegatableUDF").First() as UserDefinedFunction; - Assert.False(func.HasDelegationWarning); + var func = engine.Functions.WithName(expectedError ? "NonDelegatableUDF" : "DelegatableUDF").First() as UserDefinedFunction; - result = engine.AddUserDefinedFunction("NonDelegatableUDF2():MyDataSourceTableType = Filter(MyDataSource, NonDelegatableUDF(Value));", CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); + result = engine.AddUserDefinedFunction(formula2, CultureInfo.InvariantCulture, symbolTable: engine.EngineSymbols, allowSideEffects: true); Assert.True(result.IsSuccess); - func = engine.Functions.WithName("NonDelegatableUDF2").First() as UserDefinedFunction; - Assert.True(func.HasDelegationWarning); + func = engine.Functions.WithName(expectedError ? "NonDelegatableUDF2" : "DelegatableUDF2").First() as UserDefinedFunction; + Assert.True(func.HasDelegationWarning == expectedError); } // Binding to inner functions does not impact outer functions. From 946330d2ee991b63b1030c9e0cadb96548970986 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Mon, 21 Oct 2024 21:08:12 -0400 Subject: [PATCH 05/11] handle look up function, only limit delegation to call node args --- .../DelegationValidationStrategy.cs | 18 +++++++++++------- .../Texl/Builtins/Lookup.cs | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index ff1b6cdbb8..dfb6ee84ed 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -407,16 +407,20 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera SuggestDelegationHint(node, binding, warning, new object[] { callInfo?.Function.Name }); } + if (callInfo?.Function is UserDefinedFunction udf && node.Parent?.Parent is CallNode callNode && + (binding.GetInfo(callNode).Function is FilterFunction || binding.GetInfo(callNode).Function is LookUpFunction)) + { + if ((trackingFunction is FilterFunction filterFunc && filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, nodeInheritsRowScope: isRowScoped)) || + (trackingFunction is LookUpFunction lookUpFunc && lookUpFunc.IsValidDelegatableReductionNode(callNode, udf.Binding.Top, udf.Binding, nodeInheritsRowScope: isRowScoped))) + { + udf.SetSupportRowScopedServerDelegation(true); + return true; + } + } + if (callInfo?.Function != null && ((TexlFunction)callInfo.Function).IsRowScopedServerDelegatable(node, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope)) { return true; - } - - if (callInfo?.Function is UserDefinedFunction udf && - ((FilterFunction)trackingFunction).IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, nodeInheritsRowScope: isRowScoped)) - { - udf.SetSupportRowScopedServerDelegation(true); - return true; } var telemetryMessage = string.Format(CultureInfo.InvariantCulture, "Kind:{0}, isRowScoped:{1}", node.Kind, isRowScoped); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs index 9347d0c040..3ffa800c04 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs @@ -142,7 +142,7 @@ public override ICallNodeDelegatableNodeValidationStrategy GetCallNodeDelegation return new LookUpCallNodeDelegationStrategy(this); } - public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductionNode, TexlBinding binding) + public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductionNode, TexlBinding binding, bool nodeInheritsRowScope = false) { if (!TryGetFilterOpDelegationMetadata(callNode, binding, out var metadata)) { @@ -150,7 +150,7 @@ public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductio } // use a variation of the filter predicate logic to determine if the reduction formula is delegatable, without enforcing the return type must be boolean - return IsValidDelegatableFilterPredicateNode(reductionNode, binding, metadata, generateHints: false, enforceBoolean: false); + return IsValidDelegatableFilterPredicateNode(reductionNode, binding, metadata, generateHints: false, enforceBoolean: false, nodeInheritsRowScope: nodeInheritsRowScope); } } From ce24078b20a54076f3106d9ab46d0745d4ee9ff8 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 11:06:25 -0400 Subject: [PATCH 06/11] lookup function --- .../DelegationStrategies/DelegationValidationStrategy.cs | 6 +++--- .../Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index dfb6ee84ed..5a4811b323 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -407,11 +407,11 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera SuggestDelegationHint(node, binding, warning, new object[] { callInfo?.Function.Name }); } - if (callInfo?.Function is UserDefinedFunction udf && node.Parent?.Parent is CallNode callNode && - (binding.GetInfo(callNode).Function is FilterFunction || binding.GetInfo(callNode).Function is LookUpFunction)) + if (callInfo?.Function is UserDefinedFunction udf && node.Parent?.Parent is CallNode parentNode && + (binding.GetInfo(parentNode).Function is FilterFunction || binding.GetInfo(parentNode).Function is LookUpFunction)) { if ((trackingFunction is FilterFunction filterFunc && filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, nodeInheritsRowScope: isRowScoped)) || - (trackingFunction is LookUpFunction lookUpFunc && lookUpFunc.IsValidDelegatableReductionNode(callNode, udf.Binding.Top, udf.Binding, nodeInheritsRowScope: isRowScoped))) + (trackingFunction is LookUpFunction lookUpFunc && lookUpFunc.IsValidDelegatableReductionNode(parentNode, udf.Binding.Top, binding, nodeInheritsRowScope: isRowScoped, udfBinding: udf.Binding))) { udf.SetSupportRowScopedServerDelegation(true); return true; diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs index 3ffa800c04..0ddb1e95b6 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs @@ -142,15 +142,16 @@ public override ICallNodeDelegatableNodeValidationStrategy GetCallNodeDelegation return new LookUpCallNodeDelegationStrategy(this); } - public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductionNode, TexlBinding binding, bool nodeInheritsRowScope = false) + public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductionNode, TexlBinding binding, bool nodeInheritsRowScope = false, TexlBinding udfBinding = null) { if (!TryGetFilterOpDelegationMetadata(callNode, binding, out var metadata)) { return false; } - // use a variation of the filter predicate logic to determine if the reduction formula is delegatable, without enforcing the return type must be boolean - return IsValidDelegatableFilterPredicateNode(reductionNode, binding, metadata, generateHints: false, enforceBoolean: false, nodeInheritsRowScope: nodeInheritsRowScope); + // use a variation of the filter predicate logic to determine if the reduction formula is delegatable, without enforcing the return type must be boolean + // if reduction node is a user defined function call node, we will need to provide the udf binding instead + return IsValidDelegatableFilterPredicateNode(reductionNode, udfBinding ?? binding, metadata, generateHints: false, enforceBoolean: false, nodeInheritsRowScope: nodeInheritsRowScope); } } From b1d0d7c7258b9506e86d4fcf31d9730c063f8629 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 11:49:09 -0400 Subject: [PATCH 07/11] prevent warnings being added directly to udf --- .../DelegationStrategies/DelegationValidationStrategy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index 5a4811b323..343f0bd3ca 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -410,7 +410,7 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera if (callInfo?.Function is UserDefinedFunction udf && node.Parent?.Parent is CallNode parentNode && (binding.GetInfo(parentNode).Function is FilterFunction || binding.GetInfo(parentNode).Function is LookUpFunction)) { - if ((trackingFunction is FilterFunction filterFunc && filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, nodeInheritsRowScope: isRowScoped)) || + if ((trackingFunction is FilterFunction filterFunc && filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, generateHints: false, nodeInheritsRowScope: isRowScoped)) || (trackingFunction is LookUpFunction lookUpFunc && lookUpFunc.IsValidDelegatableReductionNode(parentNode, udf.Binding.Top, binding, nodeInheritsRowScope: isRowScoped, udfBinding: udf.Binding))) { udf.SetSupportRowScopedServerDelegation(true); From ee7a63e73b4ee50b578820e541fc73ca1393f731 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 14:29:09 -0400 Subject: [PATCH 08/11] feedback --- .../DelegationValidationStrategy.cs | 38 +++++++++---------- .../InOpDelegationStrategy.cs | 6 +-- .../OpDelegationStrategy.cs | 20 +++++----- .../UnaryOpDelegationStrategy.cs | 8 ++-- .../Functions/TexlFunction.cs | 2 +- .../Functions/UserDefinedFunction.cs | 10 ++--- .../Texl/Builtins/AsType.cs | 2 +- .../Texl/Builtins/CountIf.cs | 2 +- .../Texl/Builtins/DateTime.cs | 9 ----- .../Texl/Builtins/EndsWith.cs | 2 +- .../Texl/Builtins/Filter.cs | 2 +- .../Texl/Builtins/FilterDelegationBase.cs | 6 +-- .../Texl/Builtins/IsBlank.cs | 2 +- .../Texl/Builtins/Len.cs | 7 +--- .../Texl/Builtins/Logical.cs | 2 +- .../Texl/Builtins/Lookup.cs | 8 ++-- .../Texl/Builtins/LowerUpper.cs | 2 +- .../Texl/Builtins/NotFunction.cs | 2 +- .../Texl/Builtins/Proper.cs | 2 +- .../Texl/Builtins/StartsWith.cs | 2 +- .../Texl/Builtins/StatisticalTableFunction.cs | 4 +- .../Texl/Builtins/StringOneArgFunction.cs | 2 +- .../Texl/Builtins/Trim.cs | 9 ----- 23 files changed, 63 insertions(+), 86 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index 343f0bd3ca..0f6904b668 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -20,17 +20,17 @@ namespace Microsoft.PowerFx.Core.Functions.Delegation.DelegationStrategies { internal interface ICallNodeDelegatableNodeValidationStrategy { - bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false); + bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope, TexlFunction trackingFunction = null); } internal interface IDottedNameNodeDelegatableNodeValidationStrategy { - bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false); + bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope); } internal interface IFirstNameNodeDelegatableNodeValidationStrategy { - bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false); + bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope); } internal class DelegationValidationStrategy @@ -93,7 +93,7 @@ protected void SuggestDelegationHint(TexlNode node, TexlBinding binding) SuggestDelegationHint(node, binding, null); } - private bool IsValidRowScopedDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, out bool isRowScopedDelegationExempted) + private bool IsValidRowScopedDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope, out bool isRowScopedDelegationExempted) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -110,18 +110,18 @@ private bool IsValidRowScopedDottedNameNode(DottedNameNode node, TexlBinding bin if (node.Left.Kind == NodeKind.Call) { - return IsValidCallNode(node.Left as CallNode, binding, metadata); + return IsValidCallNode(node.Left as CallNode, binding, metadata, nodeInheritsRowScope); } if (node.Left.Kind == NodeKind.DottedName) { - return IsValidRowScopedDottedNameNode(node.Left.AsDottedName(), binding, metadata, out isRowScopedDelegationExempted); + return IsValidRowScopedDottedNameNode(node.Left.AsDottedName(), binding, metadata, nodeInheritsRowScope, out isRowScopedDelegationExempted); } return node.Left.Kind == NodeKind.FirstName; } - private bool IsValidNonRowScopedDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy) + private bool IsValidNonRowScopedDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -129,12 +129,12 @@ private bool IsValidNonRowScopedDottedNameNode(DottedNameNode node, TexlBinding if (node.Left.Kind == NodeKind.Call) { - return IsValidCallNode(node.Left as CallNode, binding, metadata); + return IsValidCallNode(node.Left as CallNode, binding, metadata, nodeInheritsRowScope); } if (node.Left.Kind == NodeKind.DottedName) { - return IsValidDottedNameNode(node.Left.AsDottedName(), binding, metadata, opDelStrategy); + return IsValidDottedNameNode(node.Left.AsDottedName(), binding, metadata, opDelStrategy, nodeInheritsRowScope); } if (node.Left.Kind == NodeKind.FirstName) @@ -177,7 +177,7 @@ private OperationCapabilityMetadata GetScopedOperationCapabilityMetadata(IDelega return delegationMetadata.FilterDelegationMetadata; } - public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) + public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -186,10 +186,10 @@ public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, Oper var isRowScoped = binding.IsRowScope(node) || nodeInheritsRowScope; if (!isRowScoped) { - return IsValidNonRowScopedDottedNameNode(node, binding, metadata, opDelStrategy); + return IsValidNonRowScopedDottedNameNode(node, binding, metadata, opDelStrategy, nodeInheritsRowScope); } - if (!IsValidRowScopedDottedNameNode(node, binding, metadata, out var isRowScopedDelegationExempted)) + if (!IsValidRowScopedDottedNameNode(node, binding, metadata, nodeInheritsRowScope, out var isRowScopedDelegationExempted)) { var telemetryMessage = string.Format(CultureInfo.InvariantCulture, "Kind:{0}, isRowScoped:{1}", node.Kind, isRowScoped); @@ -264,7 +264,7 @@ public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, Oper return opDelStrategy?.IsOpSupportedByColumn(entityCapabilityMetadata, node, entityColumnPath, binding) ?? true; } - public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) + public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -283,7 +283,7 @@ public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDel return false; } - return IsDelegatableColumnNode(node, binding, opDelStrategy, Function.FunctionDelegationCapability); + return IsDelegatableColumnNode(node, binding, opDelStrategy, Function.FunctionDelegationCapability, nodeInheritsRowScope); } private IDelegationMetadata GetCapabilityMetadata(FirstNameInfo info) @@ -314,7 +314,7 @@ private IDelegationMetadata GetCapabilityMetadata(FirstNameInfo info) } // Verifies if provided column node supports delegation. - protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, DelegationCapability capability, bool nodeInheritsRowScope = false) + protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, DelegationCapability capability, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(binding); @@ -361,7 +361,7 @@ protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, return true; } - public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) + public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope, TexlFunction trackingFunction = null) { // Functions may have their specific CallNodeDelegationStrategies (i.e. AsType, User) // so, if available, we need to ensure we use their specific delegation strategy. @@ -372,13 +372,13 @@ public virtual bool IsValidCallNode(CallNode node, TexlBinding binding, Operatio if (function != null && function != Function) { // We need to keep track of the tracking function for delegation tracking telemetry to be consistent. - return function.GetCallNodeDelegationStrategy().IsValidCallNode(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); + return function.GetCallNodeDelegationStrategy().IsValidCallNode(node, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope, trackingFunction ?? Function); } - return IsValidCallNodeInternal(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); + return IsValidCallNodeInternal(node, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope, trackingFunction ?? Function); } - protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) + protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope, TexlFunction trackingFunction = null) { Contracts.AssertValue(node); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs index 9759e43e33..f41a6fd7ec 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/InOpDelegationStrategy.cs @@ -25,7 +25,7 @@ public InOpDelegationStrategy(BinaryOpNode node, TexlFunction function) _binaryOpNode = node; } - public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false) + public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -40,7 +40,7 @@ public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadat var isRHSDelegableTable = IsRHSDelegableTable(binding, binaryOpNode, metadata); if (isRHSDelegableTable && binaryOpNode.Left is DottedNameNode dottedField && binding.GetType(dottedField.Left).HasExpandInfo) { - return base.IsSupportedOpNode(node, metadata, binding); + return base.IsSupportedOpNode(node, metadata, binding, nodeInheritsRowScope); } DName columnName = default; @@ -68,7 +68,7 @@ public override bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadat return false; } - return base.IsSupportedOpNode(node, metadata, binding); + return base.IsSupportedOpNode(node, metadata, binding, nodeInheritsRowScope); } public bool IsRHSDelegableTable(TexlBinding binding, BinaryOpNode binaryOpNode, OperationCapabilityMetadata metadata) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs index 265e0c4793..f8f8767ba5 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/OpDelegationStrategy.cs @@ -80,7 +80,7 @@ public virtual bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, T } // Verifies if given kind of node is supported by function delegation. - private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode, bool nodeInheritsRowScope = false) + private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -127,7 +127,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } var dottedNodeValStrategy = _function.GetDottedNameNodeDelegationStrategy(); - return dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy); + return dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy, nodeInheritsRowScope); } case NodeKind.Call: @@ -138,13 +138,13 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); - return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); + return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata, nodeInheritsRowScope); } case NodeKind.FirstName: { var firstNameNodeValStrategy = _function.GetFirstNameNodeDelegationStrategy(); - return firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy); + return firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy, nodeInheritsRowScope); } case NodeKind.UnaryOp: @@ -156,7 +156,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata var unaryopNode = node.AsUnaryOpLit(); var unaryOpNodeDelegationStrategy = _function.GetOpDelegationStrategy(unaryopNode.Op); - return unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding, nodeInheritsRowScope: nodeInheritsRowScope); + return unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding, nodeInheritsRowScope); } case NodeKind.BinaryOp: @@ -207,7 +207,7 @@ private bool IsColumnNode(TexlNode node, TexlBinding binding) return (node.Kind == NodeKind.FirstName) && binding.IsRowScope(node); } - private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetadata metadata, TexlBinding binding) + private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope) { Contracts.AssertValue(binaryOpNode); Contracts.AssertValue(metadata); @@ -224,7 +224,7 @@ private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetad // If rhs is a column of type DateTime and lhs is row scoped then we will need to apply the coercion on rhs. So check if coercion function date is supported or not. if (IsColumnNode(binaryOpNode.Right, binding) && binding.IsRowScope(binaryOpNode.Left)) { - return IsDelegatableColumnNode(binaryOpNode.Right.AsFirstName(), binding, null, DelegationCapability.Date); + return IsDelegatableColumnNode(binaryOpNode.Right.AsFirstName(), binding, null, DelegationCapability.Date, nodeInheritsRowScope); } // If lhs is rowscoped but not a field reference and rhs is rowscoped then we need to check if it's supported at table level. @@ -243,7 +243,7 @@ private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetad // If lhs is a column of type DateTime and RHS is also row scoped then check if coercion function date is supported or not. if (IsColumnNode(binaryOpNode.Left, binding) && binding.IsRowScope(binaryOpNode.Right)) { - return IsDelegatableColumnNode(binaryOpNode.Left.AsFirstName(), binding, null, DelegationCapability.Date); + return IsDelegatableColumnNode(binaryOpNode.Left.AsFirstName(), binding, null, DelegationCapability.Date, nodeInheritsRowScope); } // If lhs is rowscoped but not a field reference and rhs is rowscoped then we need to check if it's supported at table level. @@ -263,7 +263,7 @@ private bool DoCoercionCheck(BinaryOpNode binaryOpNode, OperationCapabilityMetad return true; } - public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope = false) + public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -322,7 +322,7 @@ public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata return true; } - if (!DoCoercionCheck(binaryOpNode, metadata, binding)) + if (!DoCoercionCheck(binaryOpNode, metadata, binding, nodeInheritsRowScope)) { SuggestDelegationHint(node, binding); return false; diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs index 446f5a6272..5880d52f1f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/UnaryOpDelegationStrategy.cs @@ -69,7 +69,7 @@ public virtual bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, T return true; } - private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope = false) + private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool nodeInheritsRowScope) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); @@ -91,7 +91,7 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } var dottedNodeValStrategy = _function.GetDottedNameNodeDelegationStrategy(); - return dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy); + return dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy, nodeInheritsRowScope); } case NodeKind.Call: @@ -102,13 +102,13 @@ private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); - return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata); + return cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata, nodeInheritsRowScope); } case NodeKind.FirstName: { var firstNameNodeValStrategy = _function.GetFirstNameNodeDelegationStrategy(); - return firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy); + return firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy, nodeInheritsRowScope); } case NodeKind.UnaryOp: diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs index d2a17e8dab..0e73690b14 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/TexlFunction.cs @@ -945,7 +945,7 @@ public virtual bool SupportsPaging(CallNode callNode, TexlBinding binding) // Returns true if function is row scoped and supports delegation. // Needs to be overriden by functions (For example, IsBlank) which are not server delegatable themselves but can become one when scoped inside a delegatable function. - public virtual bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public virtual bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs index c4047ecf4f..4a5f65c02f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs @@ -47,7 +47,7 @@ internal class UserDefinedFunction : TexlFunction, IExternalPageableSymbol, IExt public bool IsDelegatable => _binding.IsDelegatable(_binding.Top); - public bool SupportsRowScopedServerDelegation; + private bool _supportsRowScopedServerDelegation; public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) { @@ -58,9 +58,9 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) return base.IsServerDelegatable(callNode, binding) || IsDelegatable; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { - return SupportsRowScopedServerDelegation; + return _supportsRowScopedServerDelegation; } public override bool SupportsParamCoercion => true; @@ -101,7 +101,7 @@ public UserDefinedFunction(string functionName, DType returnType, TexlNode body, this._isImperative = isImperative; this.UdfBody = body; - this.SupportsRowScopedServerDelegation = false; + this._supportsRowScopedServerDelegation = false; } /// @@ -383,7 +383,7 @@ internal static bool IsRestrictedType(FormulaType ft) internal void SetSupportRowScopedServerDelegation(bool value) { - SupportsRowScopedServerDelegation = value; + _supportsRowScopedServerDelegation = value; } /// diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs index bf4022c9ac..ada070dacc 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/AsType.cs @@ -106,7 +106,7 @@ private static bool IsExternalSource(object externalDataSource) tDsInfo is IExternalTabularDataSource; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { return metadata.IsDelegationSupportedByTable(DelegationCapability.AsType); } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/CountIf.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/CountIf.cs index f9168fe3cb..a84a7a358f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/CountIf.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/CountIf.cs @@ -130,7 +130,7 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) // Validate for each predicate node. for (var i = 1; i < args.Count; i++) { - if (!IsValidDelegatableFilterPredicateNode(args[i], binding, metadata)) + if (!IsValidDelegatableFilterPredicateNode(args[i], binding, metadata, false)) { SuggestDelegationHint(callNode, binding); return false; diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs index c71e58c7db..d33556226a 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs @@ -51,15 +51,6 @@ internal abstract class ExtractDateTimeFunctionBase : BuiltinFunction public ExtractDateTimeFunctionBase(string name, TexlStrings.StringGetter description) : base(name, description, FunctionCategories.DateTime, DType.Number, 0, 1, 1, DType.DateTime) { - } - - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) - { - Contracts.AssertValue(callNode); - Contracts.AssertValue(binding); - Contracts.AssertValue(metadata); - - return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); } public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs index b6ca0f7414..4d6671f424 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/EndsWith.cs @@ -24,7 +24,7 @@ public EndsWithFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Filter.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Filter.cs index 09fc51746e..797fb66540 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Filter.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Filter.cs @@ -176,7 +176,7 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) // Validate for each predicate node. for (var i = 1; i < args.Count; i++) { - if (!IsValidDelegatableFilterPredicateNode(args[i], binding, metadata)) + if (!IsValidDelegatableFilterPredicateNode(args[i], binding, metadata, false)) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs index 369ece953c..20a9e83140 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/FilterDelegationBase.cs @@ -67,7 +67,7 @@ public override bool TryGetDelegationMetadata(CallNode node, TexlBinding binding // Determine whether a node can be delegated as part of a filter predicate. // The enforceBoolean flag determines whether to enforce the return type of the node. If the node is part of a filter predicate directly, it must return a boolean type. // If the node is used in other places inside a filter, such as in a nested LookUp reduction formula, it can return any type. - internal bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding binding, FilterOpMetadata filterMetadata, bool generateHints = true, bool enforceBoolean = true, bool nodeInheritsRowScope = false) + internal bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding binding, FilterOpMetadata filterMetadata, bool nodeInheritsRowScope, bool generateHints = true, bool enforceBoolean = true) { Contracts.AssertValue(dsNode); Contracts.AssertValue(binding); @@ -114,7 +114,7 @@ internal bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding return false; } - if (!firstNameStrategy.IsValidFirstNameNode(dsNode.AsFirstName(), binding, null)) + if (!firstNameStrategy.IsValidFirstNameNode(dsNode.AsFirstName(), binding, null, nodeInheritsRowScope)) { return false; } @@ -130,7 +130,7 @@ internal bool IsValidDelegatableFilterPredicateNode(TexlNode dsNode, TexlBinding return false; } - if (!dottedNameStrategy.IsValidDottedNameNode(dsNode.AsDottedName(), binding, filterMetadata, null)) + if (!dottedNameStrategy.IsValidDottedNameNode(dsNode.AsDottedName(), binding, filterMetadata, null, nodeInheritsRowScope)) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs index e8c6949a6c..b086a20704 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/IsBlank.cs @@ -92,7 +92,7 @@ public IsBlankFunction() yield return new[] { TexlStrings.IsBlankArg1 }; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs index 6876325039..921f7300ea 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Len.cs @@ -29,12 +29,7 @@ public LenFunction() yield return new[] { TexlStrings.LenArg1 }; } - public override DelegationCapability FunctionDelegationCapability => DelegationCapability.Length; - - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) - { - return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); - } + public override DelegationCapability FunctionDelegationCapability => DelegationCapability.Length; public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) { diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs index ffea807151..1f682a6c81 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Logical.cs @@ -85,7 +85,7 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp return fArgsValid; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs index 0ddb1e95b6..d628df3db1 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Lookup.cs @@ -102,7 +102,7 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) return false; } - return IsValidDelegatableFilterPredicateNode(args[1], binding, metadata); + return IsValidDelegatableFilterPredicateNode(args[1], binding, metadata, false); } private bool TryGetFilterOpDelegationMetadata(CallNode callNode, TexlBinding binding, out FilterOpMetadata metadata) @@ -151,7 +151,7 @@ public bool IsValidDelegatableReductionNode(CallNode callNode, TexlNode reductio // use a variation of the filter predicate logic to determine if the reduction formula is delegatable, without enforcing the return type must be boolean // if reduction node is a user defined function call node, we will need to provide the udf binding instead - return IsValidDelegatableFilterPredicateNode(reductionNode, udfBinding ?? binding, metadata, generateHints: false, enforceBoolean: false, nodeInheritsRowScope: nodeInheritsRowScope); + return IsValidDelegatableFilterPredicateNode(reductionNode, udfBinding ?? binding, metadata, nodeInheritsRowScope, generateHints: false, enforceBoolean: false); } } @@ -162,7 +162,7 @@ public LookUpCallNodeDelegationStrategy(TexlFunction function) { } - public override bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, TexlFunction trackingFunction = null, bool nodeInheritsRowScope = false) + public override bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope, TexlFunction trackingFunction = null) { var function = binding.GetInfo(node)?.Function; var args = node.Args.Children.VerifyValue(); @@ -176,7 +176,7 @@ function is LookUpFunction lookup && args.Count > 2 && return false; } - return base.IsValidCallNode(node, binding, metadata, trackingFunction ?? Function, nodeInheritsRowScope: nodeInheritsRowScope); + return base.IsValidCallNode(node, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope, trackingFunction ?? Function); } } } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs index bed2faae65..ee0435c77f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/LowerUpper.cs @@ -25,7 +25,7 @@ public LowerUpperFunction(bool isLower) public override DelegationCapability FunctionDelegationCapability => _isLower ? DelegationCapability.Lower : DelegationCapability.Upper; - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs index db876ae143..f551984d57 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/NotFunction.cs @@ -48,7 +48,7 @@ public override IOpDelegationStrategy GetOpDelegationStrategy(BinaryOp op, Power return new DefaultBinaryOpDelegationStrategy(op, BuiltinFunctionsCore.Filter); } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs index 08c8e0b5a9..85901383ce 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Proper.cs @@ -19,7 +19,7 @@ public ProperFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { return false; } diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs index 55cc063484..19b4e9d5b0 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StartsWith.cs @@ -21,7 +21,7 @@ public StartsWithFunction() { } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StatisticalTableFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StatisticalTableFunction.cs index 98a6397e78..59af39677a 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StatisticalTableFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StatisticalTableFunction.cs @@ -89,11 +89,11 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) if (binding.IsFullRecordRowScopeAccess(args[1])) { - return GetDottedNameNodeDelegationStrategy().IsValidDottedNameNode(args[1].AsDottedName(), binding, null, null); + return GetDottedNameNodeDelegationStrategy().IsValidDottedNameNode(args[1].AsDottedName(), binding, null, null, false); } var firstNameStrategy = GetFirstNameNodeDelegationStrategy().VerifyValue(); - return firstNameStrategy.IsValidFirstNameNode(args[1].AsFirstName(), binding, null); + return firstNameStrategy.IsValidFirstNameNode(args[1].AsFirstName(), binding, null, false); } public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs index 2fba30085a..9439d54a5d 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/StringOneArgFunction.cs @@ -38,7 +38,7 @@ public StringOneArgFunction(string name, TexlStrings.StringGetter description, F yield return new[] { TexlStrings.StringFuncArg1 }; } - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) + public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs index 3daf028f01..46b40b885c 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Trim.cs @@ -37,15 +37,6 @@ public TrimEndsFunction() } public override DelegationCapability FunctionDelegationCapability => DelegationCapability.Trim; - - public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata, bool nodeInheritsRowScope = false) - { - Contracts.AssertValue(callNode); - Contracts.AssertValue(binding); - Contracts.AssertValue(metadata); - - return base.IsRowScopedServerDelegatable(callNode, binding, metadata, nodeInheritsRowScope: nodeInheritsRowScope); - } } // Trim(arg:*[s]) From b0070434c32f3b7d9c4d47b75b1ce77347dd3267 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 15:23:51 -0400 Subject: [PATCH 09/11] test --- .../Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestUtils.cs b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestUtils.cs index 3f30ef8ea8..70bf4e61ce 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestUtils.cs +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/Helpers/TestUtils.cs @@ -302,7 +302,7 @@ public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) if (dataSource != null && dataSource.DelegationMetadata != null) { var metadata = dataSource.DelegationMetadata.FilterDelegationMetadata; - if (!IsValidDelegatableFilterPredicateNode(args[1], binding, metadata, false)) + if (!IsValidDelegatableFilterPredicateNode(args[1], binding, metadata, false, false)) { return false; } From 704cc02be37ad8ae34fca5023276986db7607a8b Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 16:36:10 -0400 Subject: [PATCH 10/11] feedback --- .../DelegationStrategies/DelegationValidationStrategy.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index 0f6904b668..78ac3339c0 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -407,11 +407,9 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera SuggestDelegationHint(node, binding, warning, new object[] { callInfo?.Function.Name }); } - if (callInfo?.Function is UserDefinedFunction udf && node.Parent?.Parent is CallNode parentNode && - (binding.GetInfo(parentNode).Function is FilterFunction || binding.GetInfo(parentNode).Function is LookUpFunction)) + if (callInfo?.Function is UserDefinedFunction udf && trackingFunction is FilterFunctionBase filterFunc) { - if ((trackingFunction is FilterFunction filterFunc && filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, generateHints: false, nodeInheritsRowScope: isRowScoped)) || - (trackingFunction is LookUpFunction lookUpFunc && lookUpFunc.IsValidDelegatableReductionNode(parentNode, udf.Binding.Top, binding, nodeInheritsRowScope: isRowScoped, udfBinding: udf.Binding))) + if (filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, generateHints: false, nodeInheritsRowScope: isRowScoped)) { udf.SetSupportRowScopedServerDelegation(true); return true; From 719cf293376765966f80e0b40ffd0944d7f53731 Mon Sep 17 00:00:00 2001 From: "Rick Nguyen (from Dev Box)" Date: Tue, 22 Oct 2024 17:31:41 -0400 Subject: [PATCH 11/11] add back parent check --- .../DelegationStrategies/DelegationValidationStrategy.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs index 78ac3339c0..c0117671d0 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Functions/Delegation/DelegationStrategies/DelegationValidationStrategy.cs @@ -407,7 +407,8 @@ protected bool IsValidCallNodeInternal(CallNode node, TexlBinding binding, Opera SuggestDelegationHint(node, binding, warning, new object[] { callInfo?.Function.Name }); } - if (callInfo?.Function is UserDefinedFunction udf && trackingFunction is FilterFunctionBase filterFunc) + if (callInfo?.Function is UserDefinedFunction udf && + node.Parent?.Parent is CallNode parentNode && binding.GetInfo(parentNode).Function is FilterFunctionBase filterFunc) { if (filterFunc.IsValidDelegatableFilterPredicateNode(udf.Binding.Top, udf.Binding, metadata as FilterOpMetadata, generateHints: false, nodeInheritsRowScope: isRowScoped)) {