diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Remove.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Remove.cs index 5cf13c02d8..99164a6d2f 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Remove.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Remove.cs @@ -7,17 +7,20 @@ using Microsoft.PowerFx.Core.Binding; using Microsoft.PowerFx.Core.Entities; using Microsoft.PowerFx.Core.Entities.QueryOptions; +using Microsoft.PowerFx.Core.Errors; using Microsoft.PowerFx.Core.Functions; using Microsoft.PowerFx.Core.Functions.DLP; using Microsoft.PowerFx.Core.Localization; using Microsoft.PowerFx.Core.Logging.Trackers; using Microsoft.PowerFx.Core.Types; +using Microsoft.PowerFx.Core.Types.Enums; using Microsoft.PowerFx.Core.Utils; using Microsoft.PowerFx.Syntax; namespace Microsoft.PowerFx.Core.Texl.Builtins { - internal abstract class RemoveFunctionBase : BuiltinFunction + // Remove(collection:*[], item1:![], item2:![], ..., ["All"]) + internal class RemoveFunction : BuiltinFunction, ISuggestionAwareFunction { public override bool ManipulatesCollections => true; @@ -27,6 +30,8 @@ internal abstract class RemoveFunctionBase : BuiltinFunction public override bool IsSelfContained => false; + public bool CanSuggestThisItem => true; + public override bool RequiresDataSourceScope => true; public override bool SupportsParamCoercion => false; @@ -40,9 +45,54 @@ internal abstract class RemoveFunctionBase : BuiltinFunction public override bool RequireAllParamColumns => true; - public RemoveFunctionBase(int arityMax, params DType[] paramTypes) - : base("Remove", TexlStrings.AboutRemove, FunctionCategories.Behavior, DType.EmptyTable, 0, 2, arityMax, paramTypes) + public override bool ArgMatchesDatasourceType(int argNum) + { + return argNum >= 1; + } + + public override bool TryGetTypeForArgSuggestionAt(int argIndex, out DType type) + { + if (argIndex > 0) + { + type = default; + return false; + } + + return base.TryGetTypeForArgSuggestionAt(argIndex, out type); + } + + public RemoveFunction() + : base("Remove", TexlStrings.AboutRemove, FunctionCategories.Behavior, DType.EmptyTable, 0, 2, int.MaxValue, DType.EmptyTable) + { + } + + public override IEnumerable GetSignatures() { + yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2 }; + yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2 }; + yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2 }; + } + + public override IEnumerable GetSignatures(int arity) + { + if (arity > 2) + { + return GetGenericSignatures(arity, TexlStrings.RemoveArg1, TexlStrings.RemoveArg2); + } + + return base.GetSignatures(arity); + } + + public override IEnumerable GetRequiredEnumNames() + { + return new List() { LanguageConstants.RemoveFlagsEnumString }; + } + + public override bool IsLazyEvalParam(TexlNode node, int index, Features features) + { + // First argument to mutation functions is Lazy for datasources that are copy-on-write. + // If there are any side effects in the arguments, we want those to have taken place before we make the copy. + return index == 0; } public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) @@ -64,54 +114,87 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp errors.EnsureError(args[0], TexlStrings.ErrNeedCollection_Func, Name); } - // !!!TODO New stuff. In case this doesnt work, delete from here to the end of the function - //var argCount = argTypes.Count(); - - //for (int i = 1; i < argCount; i++) - //{ - // var argType = argTypes[i]; - // var arg = args[i]; - - // if (!argType.IsAggregate) - // { - // if (argCount >= 3 && i == argCount - 1) - // { - // fValid = (context.Features.StronglyTypedBuiltinEnums && BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) || - // (!context.Features.StronglyTypedBuiltinEnums && DType.String.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)); - - // if (!fValid) - // { - // errors.EnsureError(DocumentErrorSeverity.Severe, arg, TexlStrings.ErrRemoveAllArg); - // } - // } - // else - // { - // fValid = false; - // errors.EnsureError(args[i], TexlStrings.ErrNeedRecord_Arg, args[i]); - // } - - // continue; - // } - - // var collectionAcceptsRecord = collectionType.Accepts(argType.ToTable(), exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules); - // var recordAcceptsCollection = argType.ToTable().Accepts(collectionType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules); - - // // PFxV1 is more restrictive than PA in terms of column matching. If the collection does not accept the record or vice versa, it is an error. - // if ((context.Features.PowerFxV1CompatibilityRules && (!collectionAcceptsRecord || !recordAcceptsCollection)) || - // (!context.Features.PowerFxV1CompatibilityRules && (!collectionAcceptsRecord && !recordAcceptsCollection))) - // { - // fValid = false; - // SetErrorForMismatchedColumns(collectionType, argType, arg, errors, context.Features); - // } - - // // Only warn about no-op record inputs if there are no data sources that would use reference identity for comparison. - // else if (!collectionType.AssociatedDataSources.Any() && !recordAcceptsCollection) - // { - // errors.EnsureError(DocumentErrorSeverity.Warning, args[i], TexlStrings.ErrCollectionDoesNotAcceptThisType); - // } - //} - - returnType = context.Features.PowerFxV1CompatibilityRules ? DType.Void : collectionType; + int argCount = argTypes.Length; + for (int i = 1; i < argCount; i++) + { + DType argType = argTypes[i]; + + if (!argType.IsRecord) + { + if (argCount >= 3 && i == argCount - 1) + { + if (context.AnalysisMode) + { + if (!DType.String.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) && + !BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[i], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + { + fValid = false; + errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrRemoveAllArg); + } + } + else + { + if (!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[i], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + { + fValid = false; + errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrRemoveAllArg); + } + } + + continue; + } + else + { + fValid = false; + errors.EnsureError(args[i], TexlStrings.ErrNeedRecord_Arg, args[i]); + continue; + } + } + + var collectionAcceptsRecord = collectionType.Accepts(argType.ToTable(), exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules); + var recordAcceptsCollection = argType.ToTable().Accepts(collectionType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules); + + // PFxV1 is more restrictive than PA in terms of column matching. If the collection does not accept the record or vice versa, it is an error. + // The item schema should be compatible with the collection schema. + if ((context.Features.PowerFxV1CompatibilityRules && (!collectionAcceptsRecord || !recordAcceptsCollection)) || + (!context.Features.PowerFxV1CompatibilityRules && (!collectionAcceptsRecord && !recordAcceptsCollection))) + { + fValid = false; + SetErrorForMismatchedColumns(collectionType, argType, args[i], errors, context.Features); + } + + // Only warn about no-op record inputs if there are no data sources that would use reference identity for comparison. + else if (!collectionType.AssociatedDataSources.Any() && !recordAcceptsCollection) + { + errors.EnsureError(DocumentErrorSeverity.Warning, args[i], TexlStrings.ErrCollectionDoesNotAcceptThisType); + } + + if (!context.AnalysisMode) + { + // ArgType[N] (0 GetRequiredEnumNames() + // This method returns true if there are special suggestions for a particular parameter of the function. + public override bool HasSuggestionsForParam(int argumentIndex) { - return new List() { LanguageConstants.RemoveFlagsEnumString }; - } + Contracts.Assert(argumentIndex >= 0); - public override bool IsLazyEvalParam(TexlNode node, int index, Features features) - { - // First argument to mutation functions is Lazy for datasources that are copy-on-write. - // If there are any side effects in the arguments, we want those to have taken place before we make the copy. - return index == 0; + return argumentIndex != 1; } public override IEnumerable GetIdentifierOfModifiedValue(TexlNode[] args, out TexlNode identifierNode) @@ -156,72 +235,11 @@ public override IEnumerable GetIdentifierOfModifiedValue(TexlNode[] { firstNameNode.Ident }; - return identifiers; } - public override bool IsAsyncInvocation(CallNode callNode, TexlBinding binding) - { - Contracts.AssertValue(callNode); - Contracts.AssertValue(binding); - - return Arg0RequiresAsync(callNode, binding); - } - } - - // Remove(collection:*[], item1:![], item2:![], ..., ["All"]) - internal class RemoveFunction : RemoveFunctionBase, ISuggestionAwareFunction - { - public bool CanSuggestThisItem => true; - - public override bool ArgMatchesDatasourceType(int argNum) - { - return argNum >= 1; - } - - public override bool TryGetTypeForArgSuggestionAt(int argIndex, out DType type) - { - if (argIndex > 0) - { - type = default; - return false; - } - - return base.TryGetTypeForArgSuggestionAt(argIndex, out type); - } - - public RemoveFunction() - : base(int.MaxValue, DType.EmptyTable) - { - } - - public override IEnumerable GetSignatures() - { - yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2 }; - yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2 }; - yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2, TexlStrings.RemoveArg2 }; - } - - public override IEnumerable GetSignatures(int arity) - { - if (arity > 2) - { - return GetGenericSignatures(arity, TexlStrings.RemoveArg1, TexlStrings.RemoveArg2); - } - - return base.GetSignatures(arity); - } - - // This method returns true if there are special suggestions for a particular parameter of the function. - public override bool HasSuggestionsForParam(int argumentIndex) - { - Contracts.Assert(argumentIndex >= 0); - - return argumentIndex != 1; - } - /// - /// As Remove uses the source record in it's entirety to find the entry in table, uses deepcompare at runtime, we need all fields from source. + /// As Remove uses the source record in it's entirity to find the entry in table, uses deepcompare at runtime, we need all fields from source. /// So update the selects for all columns in the source in this case except when datasource is pageable. /// In that case, we can get the info at runtime. /// @@ -321,15 +339,31 @@ protected override bool RequiresPagedDataForParamCore(TexlNode[] args, int param } // Remove(collection:*[], source:*[], ["All"]) - internal class RemoveAllFunction : RemoveFunctionBase + internal class RemoveAllFunction : BuiltinFunction { + public override bool ManipulatesCollections => true; + + public override bool ModifiesValues => true; + + public override bool IsSelfContained => false; + + public override bool RequiresDataSourceScope => true; + + public override bool SupportsParamCoercion => false; + + public override RequiredDataSourcePermissions FunctionPermission => RequiredDataSourcePermissions.Delete; + + public override bool MutatesArg(int argIndex, TexlNode arg) => argIndex == 0; + + public override bool RequireAllParamColumns => true; + public override bool ArgMatchesDatasourceType(int argNum) { return argNum == 1; } public RemoveAllFunction() - : base(3, DType.EmptyTable, DType.EmptyTable, DType.String) + : base("Remove", TexlStrings.AboutRemove, FunctionCategories.Behavior, DType.EmptyTable, 0, 2, 3, DType.EmptyTable, DType.EmptyTable, DType.String) { } @@ -339,6 +373,11 @@ public RemoveAllFunction() yield return new[] { TexlStrings.RemoveArg1, TexlStrings.RemoveAllArg2, TexlStrings.RemoveArg3 }; } + public override IEnumerable GetRequiredEnumNames() + { + return new List() { LanguageConstants.RemoveFlagsEnumString }; + } + public override bool IsLazyEvalParam(TexlNode node, int index, Features features) { // First argument to mutation functions is Lazy for datasources that are copy-on-write. @@ -346,6 +385,99 @@ public override bool IsLazyEvalParam(TexlNode node, int index, Features features return index == 0; } + public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary nodeToCoercedTypeMap) + { + Contracts.AssertValue(args); + Contracts.AssertAllValues(args); + Contracts.AssertValue(argTypes); + Contracts.Assert(args.Length == argTypes.Length); + Contracts.AssertValue(errors); + Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); + + bool fValid = base.CheckTypes(context, args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); + Contracts.Assert(returnType.IsTable); + + DType collectionType = argTypes[0]; + if (!collectionType.IsTable) + { + fValid = false; + errors.EnsureError(args[0], TexlStrings.ErrNeedTable_Func, Name); + } + + // The source to be collected must be a table. + DType sourceType = argTypes[1]; + if (!sourceType.IsTable) + { + fValid = false; + errors.EnsureError(args[1], TexlStrings.ErrNeedTable_Arg, args[1]); + } + + if (!context.AnalysisMode) + { + bool checkAggregateNames = sourceType.CheckAggregateNames(collectionType, args[1], errors, context.Features, SupportsParamCoercion); + + // The item schema should be compatible with the collection schema. + if (!checkAggregateNames) + { + fValid = false; + if (!SetErrorForMismatchedColumns(collectionType, sourceType, args[1], errors, context.Features)) + { + errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrTableDoesNotAcceptThisType); + } + } + } + + // The source schema should be compatible with the collection schema. + else if (!collectionType.Accepts(sourceType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) && !sourceType.Accepts(collectionType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + { + fValid = false; + if (!SetErrorForMismatchedColumns(collectionType, sourceType, args[1], errors, context.Features)) + { + errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrCollectionDoesNotAcceptThisType); + } + } + + if (args.Length == 3) + { + if (context.AnalysisMode) + { + if (!DType.String.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) && + !BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + { + fValid = false; + errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrRemoveAllArg); + } + } + else + { + if (!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + { + fValid = false; + errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrRemoveAllArg); + } + } + } + + if (context.AnalysisMode) + { + // Remove returns the new collection, so the return schema is the same as the collection schema. + returnType = collectionType; + } + else + { + returnType = DType.Void; + } + + return fValid; + } + + public override void CheckSemantics(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors) + { + base.CheckSemantics(binding, args, argTypes, errors); + base.ValidateArgumentIsMutable(binding, args[0], errors); + MutationUtils.CheckSemantics(binding, this, args, argTypes, errors); + } + // This method returns true if there are special suggestions for a particular parameter of the function. public override bool HasSuggestionsForParam(int argumentIndex) { @@ -354,6 +486,38 @@ public override bool HasSuggestionsForParam(int argumentIndex) return argumentIndex == 0; } + public override IEnumerable GetIdentifierOfModifiedValue(TexlNode[] args, out TexlNode identifierNode) + { + Contracts.AssertValue(args); + + identifierNode = null; + if (args.Length == 0) + { + return null; + } + + var firstNameNode = args[0]?.AsFirstName(); + identifierNode = firstNameNode; + if (firstNameNode == null) + { + return null; + } + + var identifiers = new List + { + firstNameNode.Ident + }; + return identifiers; + } + + public override bool IsAsyncInvocation(CallNode callNode, TexlBinding binding) + { + Contracts.AssertValue(callNode); + Contracts.AssertValue(binding); + + return Arg0RequiresAsync(callNode, binding); + } + public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding) { Contracts.AssertValue(callNode); diff --git a/src/strings/PowerFxResources.en-US.resx b/src/strings/PowerFxResources.en-US.resx index 998cd665d6..d21ec0e5ac 100644 --- a/src/strings/PowerFxResources.en-US.resx +++ b/src/strings/PowerFxResources.en-US.resx @@ -3294,21 +3294,41 @@ function_parameter - Second argument to the Patch function - the updates to be applied to the given rows. - Removes a specific record or records from a data source + Removes (optionally All) items from the specified 'collection'. Description of 'Remove' function. - - data_source - function_parameter - First parameter for the Remove function. The data source that contains the records that you want to remove from. Translate this string. When translating, maintain as a single word (i.e., do not add spaces). + + collection + function_parameter - First parameter of the Remove function - the name of the collection to have an item removed. - - remove_record(s) - function_parameter - One or more records to be removed. Translate this string. When translating, maintain as a single word (i.e., do not add spaces). + + item + function_parameter - Second parameter to the Remove function - the item to be removed. If provided, last argument must be 'RemoveFlags.All'. Is there a typo? {Locked=RemoveFlags.All} Error Message, RemoveFlags.All is an enum value that does not get localized. + + The collection to remove rows from. + + + A record value specifying the row to remove. + + + source + function_parameter - Second parameter to the RemoveAll function - the source of the elements to be removed. + + + A table value that specifies multiple rows to remove from the given collection. + + + all + function_parameter - Third argument for the Remove function - value indicating whether to remove all matches or only the first one. + + + An optional argument that specifies whether to remove all matches, or just the first one. + Error Display text representing the Error value of NotificationType enum (NotificationType_Error_Name). This describes showing an error notification. The possible values for this enumeration are: Error, Warning, Success, Information. @@ -4925,4 +4945,60 @@ The '{0}' value is invalid in this context. It should be a reference to either '{1}' or '{2}' scope name. {Locked='RightRecord'} + + The first argument of '{0}' should be a collection. + Error Message. + + + Incompatible type. The collection can't contain values of this type. + Error Message. + + + You might need to convert the type of the item using, for example, a Datevalue, Text, or Value function. + 1 How to fix the error. + + + Article: Create and update a collection in a canvas app + Article: Create and update a collection in a canvas app. + + + https://go.microsoft.com/fwlink/?linkid=722609 + {Locked} + + + Module: Use basic formulas + 3 crown link on basic formulas + + + https://go.microsoft.com/fwlink/?linkid=2132396 + {Locked} + + + Module: Author basic formulas with tables and records + 3 crown link on tables and records + + + https://go.microsoft.com/fwlink/?linkid=2132700 + {Locked} + + + Incorrect argument. This formula expects an optional 'All' argument or no argument. + Error Message. + + + If you’re using an Update function, for example, you might supply an optional 'All' argument at the end of the formula. This feature is available because a record might occur more than once (e.g., in a collection) to make sure that all copies of a record are updated. + 1 How to fix the error. + + + Either supply the optional 'All' argument or remove it. + 1 How to fix the error. + + + Article: Formula reference for Power Apps + Article: Formula reference for Power Apps + + + https://go.microsoft.com/fwlink/?linkid=2132478 + {Locked} + \ No newline at end of file diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove.txt b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove.txt index e69e895ef3..d2adc38985 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove.txt +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove.txt @@ -3,145 +3,4 @@ // Check MutationFunctionsTestSetup handler (PowerFxEvaluationTests.cs) for documentation. >> Collect(t1, r2);Remove(t1, r1);t1 -Table({Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) - ->> Collect(t1, r1);Collect(t1, r1);Collect(t1, r1);Collect(t1, r2);Remove(t1, r1, RemoveFlags.All);t1 -Table({Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {Field4:false}); - t1 -Errors: Error 200-206: The function 'Remove' has some invalid arguments.|Error 211-225: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'. - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {Field4:false}, "All"); - Remove(t1, {Field4:false}, RemoveFlags.All); - t1 -Error({Kind:ErrorKind.NotFound}) - ->> Collect(t1, r2); - Collect(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:true}, "All"); - Remove(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:true}, RemoveFlags.All); - t1 -Error({Kind:ErrorKind.NotFound}) - ->> Collect(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:1/0,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:1/0,Field2:"moon",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true}); - Remove(t1, {Field2:"earth"}, {Field2:"moon"}, "All"); - Remove(t1, {Field2:"earth"}, {Field2:"moon"}, RemoveFlags.All); - t1 -Error(Table({Kind:ErrorKind.NotFound},{Kind:ErrorKind.NotFound})) - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {DisplayNameField1:3,DisplayNameField2:"earth",DisplayNameField3:DateTime(2022,2,1,0,0,0,0),DisplayNameField4:false}); - t1 -Table({Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true},{Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false},{Field1:4,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {DisplayNameField1:3,DisplayNameField2:"earth",DisplayNameField3:DateTime(2022,2,1,0,0,0,0),DisplayNameField4:false}, RemoveFlags.All); - t1 -Table({Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true},{Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) - ->> Remove(t2, {Field1:1,Field2:{Field2_1:121,Field2_2:"2_2",Field2_3:{Field2_3_1:1231,Field2_3_2:"common"}},Field3:false}); t2 -Table({Field1:2,Field2:{Field2_1:221,Field2_2:"2_2",Field2_3:{Field2_3_1:2231,Field2_3_2:"common"}},Field3:false},{Field1:3,Field2:{Field2_1:321,Field2_2:"2_2",Field2_3:{Field2_3_1:3231,Field2_3_2:"common"}},Field3:true}) - ->> Remove(t2, {Field1:1,Field2:{Field2_1:121,Field2_2:"2_2",Field2_3:{Field2_3_1:1231}},Field3:false}); t2 -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-98: Missing column. Your formula is missing a column 'DisplayNameField2.DisplayNameField2_3.Field2_3_2' with a type of 'Text'. - ->> Remove(t2, {Field1:2,Field2:{Field2_1:221,Field2_3:{Field2_3_1:2231,Field2_3_2:"common"}},Field3:false}); t2 -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-103: Missing column. Your formula is missing a column 'DisplayNameField2.Field2_2' with a type of 'Text'. - ->> Remove(t2, {Field2:{Field2_1:321, Field2_2:"Moon"}}); t2 -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-51: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'. - ->> Remove(t2, {Field2:{Field2_3:{Field2_3_1:3231}}}) ; t2 -Errors: Error 11-48: Invalid argument type. Expecting a Record value, but of a different schema.|Error 11-48: Missing column. Your formula is missing a column 'DisplayNameField2_1' with a type of 'Decimal'.|Error 0-6: The function 'Remove' has some invalid arguments. - ->> Remove(t2, {Field2:{Field2_1:321,Field2_2:"2_2",Field2_3:{Field2_3_1:3231,Field2_3_2:"common"}}}); t2 -Error({Kind:ErrorKind.NotFound}) - ->> Remove(t2, {Field1:5}) -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-21: Missing column. Your formula is missing a column 'Field2' with a type of 'Record'. - ->> Remove(t2, {Field1:99,Field2:{Field2_1:555,Field2_2:"2_2",Field2_3:{Field2_3_1:3231,Field2_3_2:"common"}}}); -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-106: Missing column. Your formula is missing a column 'Field3' with a type of 'Boolean'. - ->> Remove(t2, {Field2:{Field2_1:321,Field2_2:"2_2",Field2_3:{Field2_3_1:555,Field2_3_2:"common"}}}); -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-95: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'. - - ->> Remove(t2, {Field2:{Field2_3:{Field2_3_2:"common"}}}, "All") -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-52: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'.|Error 54-59: If provided, last argument must be 'RemoveFlags.All'. Is there a typo? - ->> Remove(t2, {Field1:5}, RemoveFlags.All) -Error({Kind:ErrorKind.NotFound}) - ->> Remove(t2, {Field2:{Field2_1:321}});t2 -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-34: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'. - ->> Remove(t2, {Field2:{Field2_3:{Field2_3_1:3231}}});t2 -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-48: Missing column. Your formula is missing a column 'Field1' with a type of 'Decimal'. - ->> Remove(t2, {Field2:{Field2_1:321,Field2_2:"2_2",Field2_3:{Field2_3_1:5555,Field2_3_2:"common"}}}, RemoveFlags.All);t2 -Error({Kind:ErrorKind.NotFound}) - -// Wrong arguments ->> Remove(t1, r1,"Al"); -Errors: Error 14-18: If provided, last argument must be 'RemoveFlags.All'. Is there a typo?|Error 0-6: The function 'Remove' has some invalid arguments. - ->> Remove(t1, r1,""); -Errors: Error 14-16: If provided, last argument must be 'RemoveFlags.All'. Is there a typo?|Error 0-6: The function 'Remove' has some invalid arguments. - ->> Remove(t1, r1, r1, r1, r1, r1, r1, "Al"); -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 35-39: If provided, last argument must be 'RemoveFlags.All'. Is there a typo? - ->> Remove(t1, "All"); -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-16: Cannot use a non-record value in this context: '"All"'. - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2040,2,1,0,0,0,0),Field4:false}); - Remove(t1,LookUp(t1,DisplayNameField2="earth")); - t1 -Table({Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false},{Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true},{Field1:4,Field2:"earth",Field3:DateTime(2040,2,1,0,0,0,0),Field4:false}) - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2040,2,1,0,0,0,0),Field4:false}); - Remove(t1,Last(Filter(t1, DisplayNameField2 = "earth"))); - t1 -Table({Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true},{Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false},{Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true}) - ->> Collect(t1, r2); - Collect(t1, {Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true}); - Collect(t1, {Field1:4,Field2:"earth",Field3:DateTime(2040,2,1,0,0,0,0),Field4:false}); - Remove(t1,First(Filter(t1, DisplayNameField2 = "earth"))); - t1 -Table({Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false},{Field1:3,Field2:"earth",Field3:DateTime(2030,2,1,0,0,0,0),Field4:true},{Field1:4,Field2:"earth",Field3:DateTime(2040,2,1,0,0,0,0),Field4:false}) - ->> Remove(Foo, {Field1:5}, RemoveFlags.All) -Errors: Errors: Error 7-10: Name isn't valid. 'Foo' isn't recognized. - ->> Remove(Foo, Bar) -Errors: Error 7-10: Name isn't valid. 'Foo' isn't recognized.|Error 12-15: Name isn't valid. 'Bar' isn't recognized.|Error 0-6: The function 'Remove' has some invalid arguments. - ->> Remove(t1, {Field1:2,Field2:"not in the table",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) -Error({Kind:ErrorKind.NotFound}) - ->> Remove(t1, If(1/0<2, {Field1:2})) -Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-32: Missing column. Your formula is missing a column 'Field2' with a type of 'Text'. - -// Remove propagates error. ->> Remove(t1, If(1/0<2, {Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true})) -Error({Kind:ErrorKind.Div0}) \ No newline at end of file +Table({Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) \ No newline at end of file diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove_V1Compact.txt b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove_V1Compact.txt index b9a95d1f57..c9c871faa2 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove_V1Compact.txt +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove_V1Compact.txt @@ -1,9 +1,8 @@ -#SETUP: PowerFxV1CompatibilityRules -#SETUP: EnableExpressionChaining,MutationFunctionsTestSetup +#SETUP: PowerFxV1CompatibilityRules,EnableExpressionChaining,MutationFunctionsTestSetup,StronglyTypedBuiltinEnums // Check MutationFunctionsTestSetup handler (PowerFxEvaluationTests.cs) for documentation. ->> Remove(t2, {Field1:1,Field2:{Field2_1:121,Field2_2:"2_2",Field2_3:{Field2_3_1:1231,Field2_3_2:"common"}},Field3:false}, "All") +>> Remove(t2, {Field1:1,Field2:{Field2_1:121,Field2_2:"2_2",Field2_3:{Field2_3_1:1231,Field2_3_2:"common"}},Field3:false}, RemoveFlags.All) If(true, {test:1}, "Void value (result of the expression can't be used).") >> Collect(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); diff --git a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/MutationScripts/Remove.txt b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/MutationScripts/Remove.txt index b9024ff4d1..a81bcfeed9 100644 --- a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/MutationScripts/Remove.txt +++ b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/MutationScripts/Remove.txt @@ -1,18 +1,59 @@ -#SETUP: EnableExpressionChaining,MutationFunctionsTestSetup,StronglyTypedBuiltinEnums +#SETUP: PowerFxV1CompatibilityRules,StronglyTypedBuiltinEnums -// Check MutationFunctionsTestSetup handler (PowerFxEvaluationTests.cs) for documentation. +>> Set(t1, Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)})) +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) ->> Remove(t2, {Field1:1,Field2:{Field2_1:121,Field2_2:"2_2",Field2_3:{Field2_3_1:1231,Field2_3_2:"common"}},Field3:false}, RemoveFlags.All) +>> Set(t2, t1) +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> Remove(t1, First(t1)) If(true, {test:1}, "Void value (result of the expression can't be used).") ->> Collect(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - Remove(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) +>> 0;t1 +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> 0;Set(t1, t2) +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> Remove(t1, First(t1), RemoveFlags.All) If(true, {test:1}, "Void value (result of the expression can't be used).") ->> Collect(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}); - If(true, Remove(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false})) +>> 1;t1 +Table({a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> 1;Set(t1, t2) +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> Remove(t1, {a:true}) +Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-19: Missing column. Your formula is missing a column 'b' with a type of 'Text'. + +>> 2;t1 +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> Remove(t1, {a:true,b:"does not exist",c:Now()}) +Error({Kind:ErrorKind.NotFound}) + +>> Remove(t1, {a:true,b:"does not exist",c:Now()}, RemoveFlags.All) +Error({Kind:ErrorKind.NotFound}) + +>> Remove(t1, {a:true,b:"does not exist",c:Now()}, {a:false,b:"does not exist",c:Now()}, RemoveFlags.All) +Error(Table({Kind:ErrorKind.NotFound},{Kind:ErrorKind.NotFound})) + +>> 3;t1 +Table({a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:true,b:"hi",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hello",c:DateTime(2024,1,1,0,0,0,0)},{a:false,b:"hi",c:DateTime(2024,1,1,0,0,0,0)}) + +>> Set(t3, Table({a:{aa:{aaa:true,bbb:true}}})) +Table({a:{aa:{aaa:true,bbb:true}}}) + +>> Remove(t3, {a:{aa:{aaa:true}}}) +Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-30: Missing column. Your formula is missing a column 'a.aa.bbb' with a type of 'Boolean'.|Error 11-30: Invalid argument type. Expecting a Record value, but of a different schema.|Error 11-30: Missing column. Your formula is missing a column 'aa.bbb' with a type of 'Boolean'. + +>> Remove(t3, {a:{aa:{aaa:true,bbb:false}}}) +Error({Kind:ErrorKind.NotFound}) + +>> Remove(t3, {a:{aa:{aaa:true,bbb:true}}}) If(true, {test:1}, "Void value (result of the expression can't be used).") -// Remove propagates error. ->> Remove(t1, {Field1:2,Field2:"moon",Field3:DateTime(2022,2,1,0,0,0,0),Field4:false}) -Error({Kind:ErrorKind.NotFound}) \ No newline at end of file +>> t3 +Table() +