Skip to content

Commit 617a7fd

Browse files
Covering some corner cases. Fixing a bug when removing the same record (value) multiple times.
1 parent c3dffa0 commit 617a7fd

File tree

5 files changed

+68
-59
lines changed

5 files changed

+68
-59
lines changed

src/libraries/Microsoft.PowerFx.Core/Public/Values/CollectionTableValue.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public override DValue<RecordValue> Last(bool mutationCopy = false)
184184
public override async Task<DValue<BooleanValue>> RemoveAsync(IEnumerable<FormulaValue> recordsToRemove, bool all, CancellationToken cancellationToken)
185185
{
186186
var ret = false;
187-
var deleteList = new List<T>();
187+
var markedToDeletion = new HashSet<T>();
188188
var errors = new List<ExpressionError>();
189189

190190
cancellationToken.ThrowIfCancellationRequested();
@@ -206,9 +206,15 @@ public override async Task<DValue<BooleanValue>> RemoveAsync(IEnumerable<Formula
206206

207207
if (await MatchesAsync(dRecord.Value, recordToRemove, cancellationToken).ConfigureAwait(false))
208208
{
209-
found = true;
210-
211-
deleteList.Add(item);
209+
if (markedToDeletion.Contains(item))
210+
{
211+
continue;
212+
}
213+
else
214+
{
215+
found = true;
216+
markedToDeletion.Add(item);
217+
}
212218

213219
if (!all)
214220
{
@@ -224,7 +230,7 @@ public override async Task<DValue<BooleanValue>> RemoveAsync(IEnumerable<Formula
224230
}
225231
}
226232

227-
foreach (var delete in deleteList)
233+
foreach (var delete in markedToDeletion)
228234
{
229235
_sourceList.Remove(delete);
230236
ret = true;

src/libraries/Microsoft.PowerFx.Core/Texl/Remove.cs

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -119,34 +119,16 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp
119119

120120
if (!argType.IsRecord)
121121
{
122-
if (argCount >= 3 && i == argCount - 1)
122+
if (argCount >= 3 && i == argCount - 1 &&
123+
((context.Features.PowerFxV1CompatibilityRules && BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[i], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) ||
124+
(!context.Features.PowerFxV1CompatibilityRules && DType.String.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))))
123125
{
124-
if (context.AnalysisMode)
125-
{
126-
if (!DType.String.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
127-
!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[i], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))
128-
{
129-
fValid = false;
130-
errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrRemoveAllArg);
131-
}
132-
}
133-
else
134-
{
135-
if (!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[i], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))
136-
{
137-
fValid = false;
138-
errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrRemoveAllArg);
139-
}
140-
}
141-
142-
continue;
143-
}
144-
else
145-
{
146-
fValid = false;
147-
errors.EnsureError(args[i], TexlStrings.ErrNeedRecord_Arg, args[i]);
148126
continue;
149127
}
128+
129+
fValid = false;
130+
errors.EnsureError(args[i], TexlStrings.ErrNeedRecord_Arg, args[i]);
131+
continue;
150132
}
151133

152134
var collectionAcceptsRecord = collectionType.Accepts(argType.ToTable(), exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules);
@@ -351,7 +333,7 @@ public override bool ArgMatchesDatasourceType(int argNum)
351333
}
352334

353335
public RemoveAllFunction()
354-
: base("Remove", TexlStrings.AboutRemove, FunctionCategories.Behavior, DType.EmptyTable, 0, 2, 3, DType.EmptyTable, DType.EmptyTable, DType.String)
336+
: base("Remove", TexlStrings.AboutRemove, FunctionCategories.Behavior, DType.EmptyTable, 0, 2, 3, DType.EmptyTable, DType.EmptyTable)
355337
{
356338
}
357339

@@ -425,25 +407,12 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp
425407
}
426408
}
427409

428-
if (args.Length == 3)
410+
if (args.Length == 3 &&
411+
((context.Features.PowerFxV1CompatibilityRules && !BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) ||
412+
(!context.Features.PowerFxV1CompatibilityRules && !DType.String.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))))
429413
{
430-
if (context.AnalysisMode)
431-
{
432-
if (!DType.String.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
433-
!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))
434-
{
435-
fValid = false;
436-
errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrRemoveAllArg);
437-
}
438-
}
439-
else
440-
{
441-
if (!BuiltInEnums.RemoveFlagsEnum.FormulaType._type.Accepts(argTypes[2], exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))
442-
{
443-
fValid = false;
444-
errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrRemoveAllArg);
445-
}
446-
}
414+
fValid = false;
415+
errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrRemoveAllArg);
447416
}
448417

449418
returnType = context.Features.PowerFxV1CompatibilityRules ? DType.Void : collectionType;

src/libraries/Microsoft.PowerFx.Interpreter/Functions/Mutation/MutationUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static async Task<FormulaValue> RemoveCore(FormulaType irContext, Formula
7878

7979
if (arg is TableValue tableValue)
8080
{
81-
var errorRecord = tableValue.Rows.First(row => row.IsError);
81+
var errorRecord = tableValue.Rows.FirstOrDefault(row => row.IsError);
8282
if (errorRecord != null)
8383
{
8484
return errorRecord.Error;

src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/Remove.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
// Wrong arguments
66
>> Remove(t1, r1,"Al");
7-
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.
7+
Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 14-18: Cannot use a non-record value in this context: '"Al"'.
88

99
>> Remove(t1, r1,"");
10-
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.
10+
Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 14-16: Cannot use a non-record value in this context: '""'.
1111

1212
>> Remove(t1, r1, r1, r1, r1, r1, r1, "Al");
13-
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?
13+
Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 35-39: Cannot use a non-record value in this context: '"Al"'.
1414

1515
>> Remove(t1, "All");
1616
Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Error 11-16: Cannot use a non-record value in this context: '"All"'.

src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/MutationScripts/Remove.txt

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,58 @@ Errors: Errors: Error 0-6: The function 'Remove' has some invalid arguments.|Err
4848
>> 4;t1
4949
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)})
5050

51+
>> Set(t3, t1)
52+
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)})
53+
54+
// Removing multiple rows with the same values.
55+
>> Remove(t3, {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)})
56+
If(true, {test:1}, "Void value (result of the expression can't be used).")
57+
58+
>> 0;t3
59+
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)})
60+
61+
>> 0;Set(t3, t1)
62+
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)})
63+
64+
>> Remove(t3, {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)}, RemoveFlags.All)
65+
Error(Table({Kind:ErrorKind.NotFound},{Kind:ErrorKind.NotFound}))
66+
67+
>> 1;t3
68+
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)})
69+
70+
>> 1;Set(t3, t1)
71+
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)})
72+
73+
>> Remove(t3, t3)
74+
If(true, {test:1}, "Void value (result of the expression can't be used).")
75+
76+
>> 2;t3
77+
Table()
78+
79+
>> 2;Set(t3, t1)
80+
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)})
81+
82+
>> Remove(t3, t3, RemoveFlags.All)
83+
Error(Table({Kind:ErrorKind.NotFound},{Kind:ErrorKind.NotFound}))
84+
5185
// Remove propagates error.
5286
>> Remove(t1, If(1/0<2, {a:true,b:"hello",c:DateTime(2024,1,1,0,0,0,0)}))
5387
Error({Kind:ErrorKind.Div0})
5488

55-
>> Set(t3, Table({a:{aa:{aaa:true,bbb:true}}}))
89+
>> Set(t4, Table({a:{aa:{aaa:true,bbb:true}}}))
5690
Table({a:{aa:{aaa:true,bbb:true}}})
5791

58-
>> Remove(t3, {a:{aa:{aaa:true}}})
92+
>> Remove(t4, {a:{aa:{aaa:true}}})
5993
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'.
6094

61-
>> Remove(t3, {a:{aa:{aaa:true,bbb:false}}})
95+
>> Remove(t4, {a:{aa:{aaa:true,bbb:false}}})
6296
Error({Kind:ErrorKind.NotFound})
6397

64-
>> Remove(t3, {a:{aa:{aaa:true,bbb:false}}}, RemoveFlags.All)
98+
>> Remove(t4, {a:{aa:{aaa:true,bbb:false}}}, RemoveFlags.All)
6599
Error({Kind:ErrorKind.NotFound})
66100

67-
>> Remove(t3, {a:{aa:{aaa:true,bbb:true}}})
101+
>> Remove(t4, {a:{aa:{aaa:true,bbb:true}}})
68102
If(true, {test:1}, "Void value (result of the expression can't be used).")
69103

70-
>> t3
104+
>> t4
71105
Table()

0 commit comments

Comments
 (0)