From cb75106376b0b3e94e86dd684d4912660c98b4bc Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Wed, 5 Dec 2012 12:53:03 -0500 Subject: fdo#57497: Remove group table data when all group dimensions are gone. Otherwise ungrouping date grouped dimension may crash, or produce incorrect results. Change-Id: I3634e3c0bf8336fc1221f5d234cb7e01eb1f07c6 --- sc/inc/dpsave.hxx | 2 ++ sc/qa/unit/ucalc.cxx | 29 +++++++++++++++++++++++++++ sc/source/core/data/dpobject.cxx | 12 ++++++++++- sc/source/core/data/dpsave.cxx | 37 ++++++++++++++++++++++++++++++++++ sc/source/ui/view/dbfunc3.cxx | 43 +++++++--------------------------------- 5 files changed, 86 insertions(+), 37 deletions(-) diff --git a/sc/inc/dpsave.hxx b/sc/inc/dpsave.hxx index 14a0032150bd..4225d1b92bd7 100644 --- a/sc/inc/dpsave.hxx +++ b/sc/inc/dpsave.hxx @@ -345,6 +345,8 @@ public: const ScDPDimensionSaveData* GetExistingDimensionData() const { return pDimensionData; } + void RemoveAllGroupDimensions( const rtl::OUString& rSrcDimName, std::vector* pDeletedNames = NULL ); + SC_DLLPUBLIC ScDPDimensionSaveData* GetDimensionData(); // create if not there void SetDimensionData( const ScDPDimensionSaveData* pNew ); // copied void BuildAllDimensionMembers(ScDPTableData* pData); diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index cab47a690fc6..795bea32a682 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -2688,6 +2688,35 @@ void Test::testPivotTableDateGrouping() CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess); } + // Remove all date grouping. The source dimension "Date" has two + // external dimensions ("Years" and "Quarters") and one internal ("Date" + // the same name but different hierarchy). Remove all of them. + pSaveData = pDPObj->GetSaveData(); + pSaveData->RemoveAllGroupDimensions(aBaseDimName); + pDPObj->SetSaveData(*pSaveData); + pDPObj->ReloadGroupTableData(); + pDPObj->InvalidateData(); + + aOutRange = refresh(pDPObj); + { + // Expected output table content. 0 = empty cell + const char* aOutputCheck[][2] = { + { "Date", 0 }, + { "2011-01-01", "1" }, + { "2011-03-02", "2" }, + { "2011-09-03", "7" }, + { "2012-01-04", "3" }, + { "2012-02-23", "4" }, + { "2012-02-24", "5" }, + { "2012-03-15", "6" }, + { "2012-12-25", "8" }, + { "Total Result", "36" } + }; + + bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Remove all date grouping."); + CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess); + } + pDPs->FreeTable(pDPObj); CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast(0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.", diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx index f4b39ffcf0e4..8db3c16abf8b 100644 --- a/sc/source/core/data/dpobject.cxx +++ b/sc/source/core/data/dpobject.cxx @@ -567,8 +567,18 @@ void ScDPObject::ReloadGroupTableData() const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData(); if (!pDimData || !pDimData->HasGroupDimensions()) - // No group dimensions exist. + { + // No group dimensions exist. Check if it currently has group + // dimensions, and if so, remove all of them. + ScDPGroupTableData* pData = dynamic_cast(mpTableData.get()); + if (pData) + { + // Replace the existing group table data with the source data. + shared_ptr pSource = pData->GetSourceTableData(); + mpTableData = pSource; + } return; + } ScDPGroupTableData* pData = dynamic_cast(mpTableData.get()); if (pData) diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx index 5c987da177e8..f86dce4a5ad7 100644 --- a/sc/source/core/data/dpsave.cxx +++ b/sc/source/core/data/dpsave.cxx @@ -1221,6 +1221,43 @@ bool ScDPSaveData::IsEmpty() const return true; // no entries that are not hidden } +void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector* pDeletedNames ) +{ + if (!pDimensionData) + // No group dimensions exist. Nothing to do. + return; + + // Remove numeric group dimension (exists once at most). No need to delete + // anything in save data (grouping was done inplace in an existing base + // dimension). + pDimensionData->RemoveNumGroupDimension(rSrcDimName); + + // Remove named group dimension(s). Dimensions have to be removed from + // dimension save data and from save data too. + const ScDPSaveGroupDimension* pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName); + while ( pExistingGroup ) + { + rtl::OUString aGroupDimName = pExistingGroup->GetGroupDimName(); + pDimensionData->RemoveGroupDimension(aGroupDimName); // pExistingGroup is deleted + + // also remove SaveData settings for the dimension that no longer exists + RemoveDimensionByName(aGroupDimName); + + if (pDeletedNames) + pDeletedNames->push_back(aGroupDimName); + + // see if there are more group dimensions + pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName); + + if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName ) + { + // still get the same group dimension? + OSL_FAIL("couldn't remove group dimension"); + pExistingGroup = NULL; // avoid endless loop + } + } +} + ScDPDimensionSaveData* ScDPSaveData::GetDimensionData() { if (!pDimensionData) diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx index bd3fa1919a9c..3f3992adc6ac 100644 --- a/sc/source/ui/view/dbfunc3.cxx +++ b/sc/source/ui/view/dbfunc3.cxx @@ -973,52 +973,23 @@ void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nPar if (aEntries.empty()) return; + std::vector aDeletedNames; bool bIsDataLayout; OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); ScDPSaveData aData( *pDPObj->GetSaveData() ); ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there - // find original base + // find the source dimension name. rtl::OUString aBaseDimName = aDimName; if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) ) aBaseDimName = pBaseGroupDim->GetSourceDimName(); - // remove all existing parts (the grouping is built completely new) - - /* Remove numeric group dimension (exists once at most). No need - to delete anything in save data (grouping was done inplace in - an existing base dimension). */ - pDimData->RemoveNumGroupDimension( aBaseDimName ); - - /* Remove named group dimension(s). Collect deleted dimension - names which may be reused while recreating the groups. - Dimensions have to be removed from dimension save data and from - save data too. */ - std::vector aDeletedNames; - const ScDPSaveGroupDimension* pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName ); - while ( pExistingGroup ) - { - rtl::OUString aGroupDimName = pExistingGroup->GetGroupDimName(); - pDimData->RemoveGroupDimension( aGroupDimName ); // pExistingGroup is deleted - - // also remove SaveData settings for the dimension that no longer exists - aData.RemoveDimensionByName( aGroupDimName ); - - /* The name can be used for the new group dimensions, although - it is still in use with the DataPilotSource. */ - aDeletedNames.push_back( aGroupDimName ); - - // see if there are more group dimensions - pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName ); - - if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName ) - { - // still get the same group dimension? - OSL_FAIL("couldn't remove group dimension"); - pExistingGroup = NULL; // avoid endless loop - } - } + // Remove all group dimensions associated with this source dimension. For + // date grouping, we need to remove all existing groups for the affected + // source dimension and build new one(s) from scratch. Keep the deleted + // names so that they can be reused during re-construction. + aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames); if ( nParts ) { -- cgit v1.2.3