keep conflicting named ranges working with 16k columns
Named ranges named e.g. 'num1' are actually valid cell addresses
when using 16k columns. We prevent naming ranges in a way that would
make them conflict, but it's possible to read them from a saved
file that was created with fewer columns, and in such cases formulas
using them would silently refer to those cells instead of to
the named range. I don't see anything in the ODF spec, but OOXML
in 18.2.5 recommends this in case there are conflicts (only outside
of the normal Excel range of A1-XFD1048576, inside they are always
meant to be references, but our normal range currently is only 1k
columns, and it's simpler and probably harmless to always resolve
a conflict this way). I can optimize performance of this in another
commit if needed.
Change-Id: I46aef54b069700e7bf268b50fdc1a88989f3ee29
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130891
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index c2bdb29..15b3823 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -357,7 +357,7 @@ private:
bool ParsePredetectedReference( const OUString& rSymbol );
bool ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef );
bool ParseMacro( const OUString& );
bool ParseNamedRange( const OUString& );
bool ParseNamedRange( const OUString&, bool onlyCheck = false );
bool ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange );
bool ParseDBRange( const OUString& );
bool ParseColRowName( const OUString& );
diff --git a/sc/qa/unit/data/ods/named-range-conflict.ods b/sc/qa/unit/data/ods/named-range-conflict.ods
new file mode 100644
index 0000000..b014cad
--- /dev/null
+++ b/sc/qa/unit/data/ods/named-range-conflict.ods
Binary files differ
diff --git a/sc/qa/unit/jumbosheets-test.cxx b/sc/qa/unit/jumbosheets-test.cxx
index 76f2235..deb516a 100644
--- a/sc/qa/unit/jumbosheets-test.cxx
+++ b/sc/qa/unit/jumbosheets-test.cxx
@@ -45,6 +45,7 @@ public:
void testRoundtripColumn2000Xlsx();
void testRoundtripColumnRange();
void testRoundtripNamedRanges();
void testNamedRangeNameConflict();
void testTdf134553();
void testTdf134392();
void testTdf147509();
@@ -57,6 +58,7 @@ public:
CPPUNIT_TEST(testRoundtripColumn2000Xlsx);
CPPUNIT_TEST(testRoundtripColumnRange);
CPPUNIT_TEST(testRoundtripNamedRanges);
CPPUNIT_TEST(testNamedRangeNameConflict);
CPPUNIT_TEST(testTdf134553);
CPPUNIT_TEST(testTdf134392);
CPPUNIT_TEST(testTdf147509);
@@ -222,6 +224,30 @@ void ScJumboSheetsTest::testRoundtripNamedRanges()
xDocSh3->DoClose();
}
void ScJumboSheetsTest::testNamedRangeNameConflict()
{
// The document contains named ranges named 'num1' and 'num2', that should be still treated
// as named references even though with 16k columns those are normally NUM1 and NUM2 cells.
ScDocShellRef xDocSh = loadDoc(u"named-range-conflict.", FORMAT_ODS);
CPPUNIT_ASSERT(xDocSh.is());
ScDocument& rDoc = xDocSh->GetDocument();
rDoc.CalcAll();
CPPUNIT_ASSERT_EQUAL(0.0, rDoc.GetValue(10022, 0, 0)); // NUM1
CPPUNIT_ASSERT_EQUAL(0.0, rDoc.GetValue(10022, 1, 0)); // NUM2
CPPUNIT_ASSERT_EQUAL(2.0, rDoc.GetValue(0, 0, 0)); // = num1
CPPUNIT_ASSERT_EQUAL(3.0, rDoc.GetValue(0, 1, 0)); // = sheet2.num2
CPPUNIT_ASSERT_EQUAL(0.0, rDoc.GetValue(0, 2, 0)); // = SUM(NUM1:NUM2) (not named ranges)
rDoc.SetValue(10022, 0, 0, 100); // NUM1
rDoc.SetValue(10022, 1, 0, 200); // NUM2
rDoc.CalcAll();
// First two are the same, the sum changes.
CPPUNIT_ASSERT_EQUAL(2.0, rDoc.GetValue(0, 0, 0));
CPPUNIT_ASSERT_EQUAL(3.0, rDoc.GetValue(0, 1, 0));
CPPUNIT_ASSERT_EQUAL(300.0, rDoc.GetValue(0, 2, 0));
xDocSh->DoClose();
}
void ScJumboSheetsTest::testTdf134553()
{
ScDocShellRef xDocSh = loadDocAndSetupModelViewController(u"tdf134553.", FORMAT_XLSX);
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 0b0dce3..fca54da 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -3344,6 +3344,18 @@ bool ScCompiler::ParseSingleReference( const OUString& rName, const OUString* pE
return false;
}
// A named range named e.g. 'num1' is valid with 1k columns, but would become a reference
// when the document is opened later with 16k columns. Resolve the conflict by not
// considering it a reference.
OUString aUpper;
bool bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, rName );
if (bAsciiUpper || mbCharClassesDiffer)
aUpper = ScGlobal::getCharClass().uppercase( rName );
mnCurrentSheetTab = aAddr.Tab(); // temporarily set for ParseNamedRange()
if(ParseNamedRange( aUpper, true )) // only check
return false;
mnCurrentSheetTab = -1;
ScSingleRefData aRef;
aRef.InitAddress( aAddr );
aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
@@ -3574,7 +3586,7 @@ const ScRangeData* ScCompiler::GetRangeData( SCTAB& rSheet, const OUString& rUpp
return pData;
}
bool ScCompiler::ParseNamedRange( const OUString& rUpperName )
bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck )
{
// ParseNamedRange is called only from NextNewToken, with an upper-case string
@@ -3582,7 +3594,8 @@ bool ScCompiler::ParseNamedRange( const OUString& rUpperName )
const ScRangeData* pData = GetRangeData( nSheet, rUpperName);
if (pData)
{
maRawToken.SetName( nSheet, pData->GetIndex());
if (!onlyCheck)
maRawToken.SetName( nSheet, pData->GetIndex());
return true;
}
@@ -3596,7 +3609,8 @@ bool ScCompiler::ParseNamedRange( const OUString& rUpperName )
pData = pRangeName->findByUpperName(aName);
if (pData)
{
maRawToken.SetName( mnCurrentSheetTab, pData->GetIndex());
if (!onlyCheck)
maRawToken.SetName( mnCurrentSheetTab, pData->GetIndex());
return true;
}
}