From 5ccc5432c3e2eee399627d844b42b65f9210bb96 Mon Sep 17 00:00:00 2001 From: swmal <{ID}+username}@users.noreply.github.com> Date: Fri, 16 May 2025 10:28:30 +0200 Subject: [PATCH 1/2] #2007 - fix for CircularReferenceException with dynamic arrays --- .../DependencyChain/RpnFormulaExecution.cs | 16 +++++++++++++--- .../RefAndLookup/ChooseBaseFunction.cs | 10 ++++++++++ .../Issues/FormulaCalculationIssues.cs | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs b/src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs index ecba7c160..2500ca7f5 100644 --- a/src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs +++ b/src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs @@ -2,6 +2,7 @@ using OfficeOpenXml.Core.CellStore; using OfficeOpenXml.Core.RangeQuadTree; using OfficeOpenXml.FormulaParsing.Excel.Functions; +using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup.LookupUtils; using OfficeOpenXml.FormulaParsing.Excel.Operators; using OfficeOpenXml.FormulaParsing.Exceptions; @@ -915,16 +916,25 @@ private static RangeHashset AddOrGetRDFromWsIx(RpnOptimizedDependencyChain depCh private static void CheckCircularReferences(RpnOptimizedDependencyChain depChain, RpnFormula f, FormulaRangeAddress address, ExcelCalculationOption options) { if (f._ws == null) return; - if(f._arrayIndex>=0) + var wsIx = f._ws?.IndexInList ?? ushort.MaxValue; + if (f._arrayIndex>=0) { var sf = f._ws._sharedFormulas[f._arrayIndex]; var fa = new FormulaRangeAddress(depChain._parsingContext) { FromRow = sf.StartRow, ToRow = sf.EndRow, FromCol = sf.StartCol, ToCol = sf.EndCol, WorksheetIx = f._ws.IndexInList }; if (fa.CollidesWith(address) != eAddressCollition.No) { - throw new CircularReferenceException($"Circular reference in array formula: {fa.Address}"); + if(!options.AllowCircularReferences) + { + throw new CircularReferenceException($"Circular reference in array formula: {fa.Address}"); + } + else + { + var toCell = ExcelCellBase.GetCellId(wsIx, sf.StartRow, sf.StartCol); + var fromCell = ExcelCellBase.GetCellId(f._ws.IndexInList, f._row, f._column); + depChain._circularReferences.Add(new CircularReference(fromCell, toCell)); + } } } - var wsIx=f._ws?.IndexInList ?? ushort.MaxValue; if (address.CollidesWith(wsIx, f._row, f._column)) { var fId = ExcelCellBase.GetCellId(f._ws.IndexInList, f._row, f._column); diff --git a/src/EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/ChooseBaseFunction.cs b/src/EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/ChooseBaseFunction.cs index 3406599b3..5755be712 100644 --- a/src/EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/ChooseBaseFunction.cs +++ b/src/EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/ChooseBaseFunction.cs @@ -21,6 +21,16 @@ internal abstract class ChooseBaseFunction : ExcelFunction { public override string NamespacePrefix => "_xlfn."; public override int ArgumentMinLength => 2; + + public override ExcelFunctionParametersInfo ParametersInfo => new ExcelFunctionParametersInfo(new Func((argumentIndex) => + { + if (argumentIndex == 0) + { + return FunctionParameterInformation.IgnoreAddress; + } + return FunctionParameterInformation.Normal; + })); + protected List GetChooseColumns(IList arguments, out eErrorType? ev) { var cols = new List(); diff --git a/src/EPPlusTest/Issues/FormulaCalculationIssues.cs b/src/EPPlusTest/Issues/FormulaCalculationIssues.cs index ab8b347fd..f5d406d7b 100644 --- a/src/EPPlusTest/Issues/FormulaCalculationIssues.cs +++ b/src/EPPlusTest/Issues/FormulaCalculationIssues.cs @@ -958,6 +958,24 @@ public void s858() Assert.AreEqual(12977661.57, result2); } + + [TestMethod] + public void Issue864() + { + using var p1 = new ExcelPackage(@"C:\Temp\EPPlus Supportcases\Supportcase864\Test.xlsx"); + var sheet = p1.Workbook.Worksheets["Aico data"]; + try + { + sheet.Calculate(o => o.AllowCircularReferences = true); + } + catch(Exception ex) + { + int i = 0; + } + + var f = sheet.Cells["C42"].Formula; + var v = sheet.Cells["C42"].Value; + } } } From 5fff73d8981d58eaf468e5069a35a5f3cfae1aac Mon Sep 17 00:00:00 2001 From: swmal <{ID}+username}@users.noreply.github.com> Date: Fri, 16 May 2025 10:35:31 +0200 Subject: [PATCH 2/2] #2007 - Fixed unittest --- src/EPPlusTest/Issues/FormulaCalculationIssues.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EPPlusTest/Issues/FormulaCalculationIssues.cs b/src/EPPlusTest/Issues/FormulaCalculationIssues.cs index f5d406d7b..9949eae00 100644 --- a/src/EPPlusTest/Issues/FormulaCalculationIssues.cs +++ b/src/EPPlusTest/Issues/FormulaCalculationIssues.cs @@ -962,7 +962,7 @@ public void s858() [TestMethod] public void Issue864() { - using var p1 = new ExcelPackage(@"C:\Temp\EPPlus Supportcases\Supportcase864\Test.xlsx"); + using var p1 = OpenTemplatePackage(@"sc864.xlsx"); var sheet = p1.Workbook.Worksheets["Aico data"]; try {