sc/inc/dbdata.hxx                            |    9 -
 sc/inc/document.hxx                          |    2 
 sc/inc/globstr.hrc                           |    1 
 sc/inc/subtotalparam.hxx                     |   25 +++
 sc/inc/table.hxx                             |    2 
 sc/inc/tokenarray.hxx                        |    1 
 sc/source/core/data/documen3.cxx             |   16 +-
 sc/source/core/data/subtotalparam.cxx        |   77 +++++++++--
 sc/source/core/data/table3.cxx               |   51 +++----
 sc/source/core/tool/dbdata.cxx               |  176 +++++++++++++++------------
 sc/source/core/tool/token.cxx                |    5 
 sc/source/filter/excel/xedbdata.cxx          |    7 -
 sc/source/filter/inc/tablecolumnsbuffer.hxx  |   16 +-
 sc/source/filter/inc/tablecolumnscontext.hxx |    1 
 sc/source/filter/oox/tablecolumnsbuffer.cxx  |   52 ++++++-
 sc/source/filter/oox/tablecolumnscontext.cxx |   23 +++
 sc/source/ui/docshell/dbdocfun.cxx           |   22 ---
 sc/source/ui/view/gridwin.cxx                |    8 -
 sc/source/ui/view/tableshell.cxx             |    8 -
 19 files changed, 316 insertions(+), 186 deletions(-)

New commits:
commit 07fa6e32e261c4b03b4760c086304a0bc61d913e
Author:     Balazs Varga <[email protected]>
AuthorDate: Wed Oct 29 14:54:20 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Mon Nov 17 19:03:09 2025 +0100

    Handle custom functions in Total row of table styles
    
    Also rework and better (more dynamic) handling of
    Total row. Also fix the additional ooxml import
    problems around Total row.
    
    cherry-pick from: bca5864a3245021601991e9be8ff6401ffd812b5
    
    Change-Id: I894c309362cfaf525ebbf47e7f49bc67b8da8495
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193205
    Reviewed-by: Balazs Varga <[email protected]>
    Tested-by: Balazs Varga <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193681
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Andras Timar <[email protected]>

diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index 66fabcccd472..0676e6d8dde5 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -83,6 +83,7 @@ struct TableColumnAttributes
 {
     std::optional<OUString> maTotalsRowLabel = std::nullopt;
     std::optional<OUString> maTotalsFunction = std::nullopt;
+    std::optional<OUString> maCustomFunction = std::nullopt;
 };
 
 // xmlColumnPr attributes
@@ -174,7 +175,6 @@ private:
     bool            bModified;          ///< is set/cleared for/by(?) 
UpdateReference
 
     ::std::vector< OUString > maTableColumnNames;   ///< names of table columns
-    ::std::vector< TableColumnAttributes > maTableColumnAttributes; ///< 
attributes of table columns
     ::std::vector< TableColumnModel > maTableColumnModel;
     bool            mbTableColumnNamesDirty;
     SCSIZE          nFilteredRowCount;
@@ -232,8 +232,6 @@ public:
     void        EndTableColumnNamesListener();
     SC_DLLPUBLIC void SetTableColumnNames( ::std::vector< OUString >&& rNames 
);
     SC_DLLPUBLIC const ::std::vector< OUString >& GetTableColumnNames() const 
{ return maTableColumnNames; }
-    SC_DLLPUBLIC void SetTableColumnAttributes( ::std::vector< 
TableColumnAttributes >&& rAttributes );
-    SC_DLLPUBLIC const ::std::vector< TableColumnAttributes >& 
GetTableColumnAttributes() const { return maTableColumnAttributes; }
     SC_DLLPUBLIC void SetTableColumnModel( TableColumnModel& rModel )
     {
         maTableColumnModel.push_back(std::move(rModel));
@@ -278,6 +276,9 @@ public:
 
     SC_DLLPUBLIC void       GetSubTotalParam(ScSubTotalParam& rSubTotalParam) 
const;
     SC_DLLPUBLIC void       SetSubTotalParam(const ScSubTotalParam& 
rSubTotalParam);
+    SC_DLLPUBLIC void       ImportSubTotalParam(ScSubTotalParam& 
rSubTotalParam,
+                                                const 
std::vector<TableColumnAttributes>& rAttributesVector,
+                                                
formula::FormulaGrammar::Grammar eGrammar) const;
     SC_DLLPUBLIC void       CreateSubTotalParam(ScSubTotalParam& 
rSubTotalParam) const;
 
     void        GetImportParam(ScImportParam& rImportParam) const;
@@ -317,7 +318,7 @@ public:
 
 private:
 
-    void AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, 
SCCOL nCol1,
+    void AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx, 
SCCOL nCol1,
             SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 );
     void InvalidateTableColumnNames( bool bSwapToEmptyNames );
 };
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 7d7caeac88b6..b6e5c9a7fbf0 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1213,7 +1213,7 @@ public:
     bool            DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     void            RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     // Table SubTotals
-    bool            DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
sal_uInt16 nIndex );
+    bool            DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     void            RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
const ScSubTotalParam& rOldParam );
 
     bool            TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& 
rParam );
@@ -2265,7 +2265,7 @@ public:
     void               Reorder( const sc::ReorderParam& rParam );
 
     void               PrepareQuery( SCTAB nTab, ScQueryParam& rQueryParam );
-    SCSIZE             Query( SCTAB nTab, const ScQueryParam& rQueryParam, 
bool bKeepSub );
+    SCSIZE             Query( SCTAB nTab, const ScQueryParam& rQueryParam, 
bool bKeepSub, bool bKeepTotals );
     SC_DLLPUBLIC bool  CreateQueryParam( const ScRange& rRange, ScQueryParam& 
rQueryParam );
     OUString           GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab);
 
diff --git a/sc/inc/subtotalparam.hxx b/sc/inc/subtotalparam.hxx
index 63a9fcb4ebe5..fcfc8ef04244 100644
--- a/sc/inc/subtotalparam.hxx
+++ b/sc/inc/subtotalparam.hxx
@@ -10,6 +10,7 @@
 #pragma once
 
 #include "global.hxx"
+#include "tokenarray.hxx"
 #include <memory>
 #include <span>
 
@@ -40,13 +41,16 @@ struct SC_DLLPUBLIC ScSubTotalParam
         bool  bActive    = false; ///< active groups
         SCCOL nField     = 0;     ///< associated field
         SCCOL nSubTotals = 0;     ///< number of SubTotals
+        SCCOL nCustFuncs = 0;     ///< number of Custom Functions
         SCCOL nSubLabels = 0;     ///< number of SubLabels
 
         using Pair = std::pair<SCCOL, ScSubTotalFunc>;
+        using FuncPair = std::pair<SCCOL, std::unique_ptr<ScTokenArray>>;
         using LabelPair = std::pair<SCCOL, rtl::OUString>;
         // array of columns to be calculated, and associated functions
         std::unique_ptr<Pair[]> pSubTotals;
-        std::unique_ptr<LabelPair[]> pSubLabels;
+        std::unique_ptr<FuncPair[]> pCustFuncs; // custom functions
+        std::unique_ptr<LabelPair[]> pSubLabels; // Total labels
 
         SubtotalGroup() = default;
         SubtotalGroup(const SubtotalGroup& r);
@@ -55,9 +59,11 @@ struct SC_DLLPUBLIC ScSubTotalParam
         bool operator==(const SubtotalGroup& r) const;
 
         void AllocSubTotals(SCCOL n);
+        void AllocCustFuncs(SCCOL n);
         void AllocSubLabels(SCCOL n);
         void SetSubtotals(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
-        void SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
+        //void SetCustFuncs(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
+        //void SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
 
         // Totals
         std::span<Pair> subtotals() { return std::span(pSubTotals.get(), 
nSubTotals); }
@@ -65,6 +71,12 @@ struct SC_DLLPUBLIC ScSubTotalParam
         SCCOL& col(SCCOL n) { return subtotals()[n].first; }
         SCCOL col(SCCOL n) const { return subtotals()[n].first; }
         ScSubTotalFunc func(SCCOL n) const { return subtotals()[n].second; }
+        // Total Functions
+        std::span<FuncPair> custfuncs() { return std::span(pCustFuncs.get(), 
nCustFuncs); }
+        std::span<const FuncPair> custfuncs() const { return 
std::span(pCustFuncs.get(), nCustFuncs); }
+        SCCOL& colcust(SCCOL n) { return custfuncs()[n].first; }
+        SCCOL colcust(SCCOL n) const { return custfuncs()[n].first; }
+        ScTokenArray* custToken(SCCOL n) const { return 
custfuncs()[n].second.get(); }
         // Labels
         std::span<LabelPair> sublabels() { return std::span(pSubLabels.get(), 
nSubLabels); }
         std::span<const LabelPair> sublabels() const { return 
std::span(pSubLabels.get(), nSubLabels); }
@@ -77,15 +89,20 @@ struct SC_DLLPUBLIC ScSubTotalParam
     ScSubTotalParam() = default;
     ScSubTotalParam(const ScSubTotalParam&) = default;
 
+    ScSubTotalParam(ScSubTotalParam&&) noexcept = default;
+    ScSubTotalParam& operator=(ScSubTotalParam&&) noexcept = default;
+
     ScSubTotalParam& operator=(const ScSubTotalParam&) = default;
     inline bool operator==(const ScSubTotalParam&) const = default;
     void SetSubTotals( sal_uInt16 nGroup,
                        const SCCOL* ptrSubTotals,
                        const ScSubTotalFunc* ptrFunctions,
                        sal_uInt16 nCount );
+    void SetCustFuncs( sal_uInt16 nGroup,
+                       std::vector<std::pair<SCCOL, 
std::unique_ptr<ScTokenArray>>>& rColFuncs,
+                       sal_uInt16 nCount );
     void SetSubLabels( sal_uInt16 nGroup,
-                       const SCCOL* ptrSubLabels,
-                       const rtl::OUString* ptrSubNames,
+                       std::vector<std::pair<SCCOL, rtl::OUString>>& 
rColLabels,
                        sal_uInt16 nCount );
 };
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index a361d4f5ea0c..9fdd7501bfbc 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -330,7 +330,7 @@ public:
     void        RemoveSubTotals( ScSubTotalParam& rParam );
     void        RemoveSimpleSubTotals( ScSubTotalParam& rParam, const 
ScSubTotalParam& rOldParam );
     bool        DoSubTotals( ScSubTotalParam& rParam );
-    bool        DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex 
);
+    bool        DoSimpleSubTotals( ScSubTotalParam& rParam );
 
     const ScSheetEvents* GetSheetEvents() const              { return 
pSheetEvents.get(); }
     void        SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew );
@@ -1043,7 +1043,7 @@ public:
     // For ValidQuery() see ScQueryEvalutor class.
     void        TopTenQuery( ScQueryParam& );
     void        PrepareQuery( ScQueryParam& rQueryParam );
-    SCSIZE      Query(const ScQueryParam& rQueryParam, bool bKeepSub);
+    SCSIZE      Query(const ScQueryParam& rQueryParam, bool bKeepSub, bool 
bKeepTotals);
     bool        CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW 
nRow2, ScQueryParam& rQueryParam);
 
     void GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, 
ScFilterEntries& rFilterEntries, bool bFiltering = false);
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
index 244d2a95341d..9843ccebf58d 100644
--- a/sc/source/core/data/documen3.cxx
+++ b/sc/source/core/data/documen3.cxx
@@ -793,10 +793,10 @@ void ScDocument::RemoveTableSubTotals( SCTAB nTab, 
ScSubTotalParam& rParam, cons
         pTable->RemoveSimpleSubTotals( rParam, rOldParam );
 }
 
-bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
sal_uInt16 nIndex )
+bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
 {
     ScTable* pTable = FetchTable(nTab);
-    return pTable && pTable->DoSimpleSubTotals(rParam, nIndex);
+    return pTable && pTable->DoSimpleSubTotals(rParam);
 }
 
 bool ScDocument::HasSubTotalCells( const ScRange& rRange )
@@ -1483,10 +1483,10 @@ void ScDocument::PrepareQuery( SCTAB nTab, 
ScQueryParam& rQueryParam )
     }
 }
 
-SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool 
bKeepSub)
+SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool 
bKeepSub, bool bKeepTotals)
 {
     if (ScTable* pTable = FetchTable(nTab))
-        return pTable->Query(rQueryParam, bKeepSub);
+        return pTable->Query(rQueryParam, bKeepSub, bKeepTotals);
 
     OSL_FAIL("missing tab");
     return 0;
diff --git a/sc/source/core/data/subtotalparam.cxx 
b/sc/source/core/data/subtotalparam.cxx
index 781480b9b02f..be9a95fc6767 100644
--- a/sc/source/core/data/subtotalparam.cxx
+++ b/sc/source/core/data/subtotalparam.cxx
@@ -28,6 +28,19 @@ ScSubTotalParam::SubtotalGroup::SubtotalGroup(const 
SubtotalGroup& r)
         std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get());
     }
 
+    if (r.nCustFuncs > 0)
+    {
+        assert(r.pCustFuncs);
+        AllocCustFuncs(r.nCustFuncs);
+        for (SCCOL i = 0; i < r.nCustFuncs; ++i)
+        {
+            if (r.pCustFuncs[i].second)
+                pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, 
r.pCustFuncs[i].second->Clone());
+            else
+                pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, nullptr);
+        }
+    }
+
     if (r.nSubLabels > 0)
     {
         assert(r.pSubLabels);
@@ -48,6 +61,19 @@ ScSubTotalParam::SubtotalGroup& 
ScSubTotalParam::SubtotalGroup::operator=(const
         std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get());
     }
 
+    AllocCustFuncs(r.nCustFuncs);
+    if (r.nCustFuncs > 0)
+    {
+        assert(r.pCustFuncs);
+        for (SCCOL i = 0; i < r.nCustFuncs; ++i)
+        {
+            if (r.pCustFuncs[i].second)
+                pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, 
r.pCustFuncs[i].second->Clone());
+            else
+                pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, nullptr);
+        }
+    }
+
     AllocSubLabels(r.nSubLabels);
     if (r.nSubLabels > 0)
     {
@@ -63,6 +89,8 @@ bool ScSubTotalParam::SubtotalGroup::operator==(const 
SubtotalGroup& r) const
     return bActive == r.bActive && nField == r.nField && nSubTotals == 
r.nSubTotals && nSubLabels == r.nSubLabels
            && (!nSubTotals
                || std::equal(pSubTotals.get(), pSubTotals.get() + nSubTotals, 
r.pSubTotals.get()))
+           && (!nCustFuncs
+               || std::equal(pCustFuncs.get(), pCustFuncs.get() + nCustFuncs, 
r.pCustFuncs.get()))
            && (!nSubLabels
                || std::equal(pSubLabels.get(), pSubLabels.get() + nSubLabels, 
r.pSubLabels.get()));
 }
@@ -76,6 +104,15 @@ void ScSubTotalParam::SubtotalGroup::AllocSubTotals(SCCOL n)
     }
 }
 
+void ScSubTotalParam::SubtotalGroup::AllocCustFuncs(SCCOL n)
+{
+    if (nCustFuncs != n)
+    {
+        nCustFuncs = std::max(n, SCCOL(0));
+        pCustFuncs.reset(nCustFuncs ? new std::pair<SCCOL, 
std::unique_ptr<ScTokenArray>>[nCustFuncs] : nullptr);
+    }
+}
+
 void ScSubTotalParam::SubtotalGroup::AllocSubLabels(SCCOL n)
 {
     if (nSubLabels != n)
@@ -93,10 +130,15 @@ void ScSubTotalParam::SubtotalGroup::SetSubtotals(const 
css::uno::Sequence<css::
                           
ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(seq[i].Function)) };
 }
 
-void ScSubTotalParam::SubtotalGroup::SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/)
-{
-    // TODO UNO::API: SubTotalColumn has no LabelName member
-}
+//void ScSubTotalParam::SubtotalGroup::SetCustFuncs(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/)
+//{
+// TODO UNO::API: SubTotalColumn has no token array member
+//}
+
+//void ScSubTotalParam::SubtotalGroup::SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/)
+//{
+// TODO UNO::API: SubTotalColumn has no LabelName member
+//}
 
 void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup,
                                     const SCCOL* ptrSubTotals,
@@ -124,26 +166,33 @@ void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup,
         aGroups[nGroup].pSubTotals[i] = { ptrSubTotals[i], ptrFunctions[i] };
 }
 
+void ScSubTotalParam::SetCustFuncs(sal_uInt16 nGroup,
+                       std::vector<std::pair<SCCOL, 
std::unique_ptr<ScTokenArray>>>& rColFuncs,
+                       sal_uInt16 nCount )
+{
+    OSL_ENSURE((nGroup <= MAXSUBTOTAL), "ScSubTotalParam::SetCustFuncs(): 
nGroup > MAXSUBTOTAL!");
+    OSL_ENSURE((nCount > 0), "ScSubTotalParam::SetCustFuncs(): nCount == 0!");
+    if (!(nCount > 0 && nGroup <= MAXSUBTOTAL))
+        return;
+
+    aGroups[nGroup].AllocCustFuncs(nCount);
+    for (sal_uInt16 i = 0; i < nCount; i++)
+        aGroups[nGroup].pCustFuncs[i] = std::make_pair(rColFuncs[i].first, 
std::move(rColFuncs[i].second));
+}
+
 void ScSubTotalParam::SetSubLabels(sal_uInt16 nGroup,
-                       const SCCOL* ptrSubLabels,
-                       const rtl::OUString* ptrSubNames,
+                       std::vector<std::pair<SCCOL, rtl::OUString>>& 
rColLabels,
                        sal_uInt16 nCount )
 {
     OSL_ENSURE((nGroup <= MAXSUBTOTAL), "ScSubTotalParam::SetSubLabels(): 
nGroup > MAXSUBTOTAL!");
-    OSL_ENSURE(ptrSubLabels, "ScSubTotalParam::SetSubLabels(): ptrSubLabels == 
NULL!");
-    OSL_ENSURE(ptrSubNames, "ScSubTotalParam::SetSubLabels(): ptrSubNames == 
NULL!");
     OSL_ENSURE((nCount > 0), "ScSubTotalParam::SetSubLabels(): nCount == 0!");
 
-    if (!(ptrSubLabels && ptrSubNames && (nCount > 0) && (nGroup <= 
MAXSUBTOTAL)))
+    if (!(nCount > 0 && nGroup <= MAXSUBTOTAL))
         return;
 
-    // 0 is interpreted as 1, otherwise decrementing the array index
-    if (nGroup != 0)
-        nGroup--;
-
     aGroups[nGroup].AllocSubLabels(nCount);
     for (sal_uInt16 i = 0; i < nCount; i++)
-        aGroups[nGroup].pSubLabels[i] = { ptrSubLabels[i], ptrSubNames[i] };
+        aGroups[nGroup].pSubLabels[i] = std::make_pair(rColLabels[i].first, 
std::move(rColLabels[i].second));
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index ea9ddcffdc02..be8931c3ab1e 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -2302,7 +2302,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
     return bSpaceLeft;
 }
 
-bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex )
+bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam )
 {
     RowEntry aRowEntry;
     aRowEntry.nGroupNo = 0;
@@ -2330,38 +2330,27 @@ bool ScTable::DoSimpleSubTotals( ScSubTotalParam& 
rParam, sal_uInt16 nIndex )
     }
     else
     {
-        SetString(group.nField, aRowEntry.nDestRow, nTab, 
ScResId(STR_TABLE_TOTAL));
+        SetString(rParam.nCol1, aRowEntry.nDestRow, nTab, 
ScResId(STR_TABLE_TOTAL));
     }
 
     // insert the formulas
-    if (group.nSubTotals > 0)
+    if (group.nCustFuncs > 0)
     {
-        for (SCCOL nResult = 0; nResult < group.nSubTotals; ++nResult)
+        for (SCCOL nResult = 0; nResult < group.nCustFuncs; ++nResult)
         {
-            ScTokenArray aArr(rDocument);
-            aArr.AddOpCode(ocSubTotal);
-            aArr.AddOpCode(ocOpen);
-            aArr.AddDouble(static_cast<double>(group.func(nResult)));
-            aArr.AddOpCode(ocSep);
-            // Table refs structure
-            aArr.AddTableRef(nIndex);
-            aArr.AddOpCode(ocTableRefOpen);
-            ScSingleRefData aSingleRef;
-            aSingleRef.InitAddress(group.col(nResult), aRowEntry.nFuncStart - 
1, nTab);
-            aArr.AddSingleReference(aSingleRef);
-            aArr.AddOpCode(ocTableRefClose);
-            // Table refs structure end
-            aArr.AddOpCode(ocClose);
-            aArr.AddOpCode(ocStop);
-            ScFormulaCell* pCell = new ScFormulaCell(
-                rDocument, ScAddress(group.col(nResult), aRowEntry.nDestRow, 
nTab), aArr);
-            if (rParam.bIncludePattern)
-                pCell->SetNeedNumberFormat(true);
-
-            SetFormulaCell(group.col(nResult), aRowEntry.nDestRow, pCell);
-            if (group.col(nResult) != group.nField)
+            if (ScTokenArray* pArray = group.custToken(nResult))
             {
-                lcl_RemoveNumberFormat(this, group.col(nResult), 
aRowEntry.nDestRow);
+                ScFormulaCell* pCell = new ScFormulaCell(
+                    rDocument, ScAddress(group.colcust(nResult), 
aRowEntry.nDestRow, nTab),
+                    *pArray);
+                if (rParam.bIncludePattern)
+                    pCell->SetNeedNumberFormat(true);
+
+                SetFormulaCell(group.colcust(nResult), aRowEntry.nDestRow, 
pCell);
+                if (group.colcust(nResult) != group.nField)
+                {
+                    lcl_RemoveNumberFormat(this, group.colcust(nResult), 
aRowEntry.nDestRow);
+                }
             }
         }
     }
@@ -2616,7 +2605,7 @@ void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
     lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
 }
 
-SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
+SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub, bool 
bKeepTotals)
 {
     ScQueryParam    aParam( rParamOrg );
     typedef std::unordered_set<OUString> StrSetType;
@@ -2649,6 +2638,11 @@ SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, 
bool bKeepSub)
     {
         bool bResult;                                   // Filter result
         bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos);
+        // Keep Totals row (last) even if we have no any cell formula!
+        if (!bValid && bKeepTotals && j == nRealRow2)
+        {
+            bValid = true;
+        }
         if (!bValid && bKeepSub)                        // Keep subtotals
         {
             for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; 
nCol++)
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 0d00773be892..14d7226ef7c6 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -25,6 +25,7 @@
 #include <unotools/charclass.hxx>
 
 #include <dbdata.hxx>
+#include <compiler.hxx>
 #include <globalnames.hxx>
 #include <refupdat.hxx>
 #include <document.hxx>
@@ -321,7 +322,6 @@ ScDBData::ScDBData( const ScDBData& rData ) :
     bAutoFilter         (rData.bAutoFilter),
     bModified           (rData.bModified),
     maTableColumnNames  (rData.maTableColumnNames),
-    maTableColumnAttributes(rData.maTableColumnAttributes),
     mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty),
     nFilteredRowCount   (rData.nFilteredRowCount)
 {
@@ -359,7 +359,6 @@ ScDBData::ScDBData( const OUString& rName, const ScDBData& 
rData ) :
     bAutoFilter         (rData.bAutoFilter),
     bModified           (rData.bModified),
     maTableColumnNames  (rData.maTableColumnNames),
-    maTableColumnAttributes(rData.maTableColumnAttributes),
     mbTableColumnNamesDirty (rData.mbTableColumnNamesDirty),
     nFilteredRowCount   (rData.nFilteredRowCount)
 {
@@ -417,7 +416,6 @@ ScDBData& ScDBData::operator= (const ScDBData& rData)
         else
         {
             maTableColumnNames  = rData.maTableColumnNames;
-            maTableColumnAttributes = rData.maTableColumnAttributes;
             mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty;
         }
 
@@ -697,7 +695,9 @@ void ScDBData::SetSubTotalParam(const ScSubTotalParam& 
rSubTotalParam)
     mpSubTotal.reset(new ScSubTotalParam(rSubTotalParam));
 }
 
-void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const
+void ScDBData::ImportSubTotalParam(ScSubTotalParam& rSubTotalParam,
+                                   const std::vector<TableColumnAttributes>& 
rAttributesVector,
+                                   formula::FormulaGrammar::Grammar eGrammar) 
const
 {
     rSubTotalParam.bDoSort = false;
     rSubTotalParam.bGroupedBy = false;
@@ -706,78 +706,112 @@ void ScDBData::CreateSubTotalParam(ScSubTotalParam& 
rSubTotalParam) const
     const size_t nEntryCount = rSubTotalParam.nCol2 - rSubTotalParam.nCol1 + 
1; // col count
     if (nEntryCount > 0)
     {
-        // how many col we do subtotal
-        size_t nTotalsCount = std::count_if(
-            GetTableColumnAttributes().begin(), 
GetTableColumnAttributes().end(),
-            [](const TableColumnAttributes& attr) { return 
attr.maTotalsFunction.has_value(); });
-        if (nTotalsCount > 0)
+        SCCOL nCol = rSubTotalParam.nCol1;
+        std::vector<std::pair<SCCOL, OUString>> vColLabels;
+        std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>> vColFuncs;
+        for (const auto& rxTableColumn : rAttributesVector)
         {
-            std::unique_ptr<ScSubTotalFunc[]> pFunctions;
-            std::unique_ptr<SCCOL[]> pSubTotals;
-            pFunctions.reset(new ScSubTotalFunc[nTotalsCount]);
-            pSubTotals.reset(new SCCOL[nTotalsCount]);
-
-            for (size_t i = 0, nCheck = 0; i < nEntryCount; i++)
+            if (rxTableColumn.maTotalsRowLabel.has_value())
+            {
+                OUString aStr = rxTableColumn.maTotalsRowLabel.value();
+                if (!aStr.isEmpty())
+                    vColLabels.push_back(std::make_pair(nCol, 
std::move(aStr)));
+            }
+            else if (mpContainer && rxTableColumn.maTotalsFunction.has_value())
             {
-                if (GetTableColumnAttributes().size() <= i)
+                ScDocument& rDoc = mpContainer->GetDocument();
+                const OUString& sFuncName = 
rxTableColumn.maTotalsFunction.value();
+                if (sFuncName == u"custom")
                 {
-                    SAL_WARN("sc.core",
-                             "ScDBData::CreateSubTotalParam - column 
attributes size mismatch");
-                    break;
+                    if (rxTableColumn.maCustomFunction.has_value())
+                    {
+                        SCROW nLastRow = rSubTotalParam.nRow2;
+                        if (!HasTotals())
+                            nLastRow++;
+
+                        ScAddress aPos(nCol, nLastRow, nTable);
+                        ScCompiler aComp(rDoc, aPos, eGrammar, true,
+                                         false);
+                        std::unique_ptr<ScTokenArray> pArr
+                            = 
aComp.CompileString(rxTableColumn.maCustomFunction.value());
+                        if (pArr)
+                        {
+                            vColFuncs.push_back(std::make_pair(nCol, 
std::move(pArr)));
+                        }
+                    }
                 }
-                if (GetTableColumnAttributes()[i].maTotalsFunction.has_value())
+                else
                 {
-                    pSubTotals[nCheck] = rSubTotalParam.nCol1 + i;
-                    const OUString& sFuncName = 
GetTableColumnAttributes()[i].maTotalsFunction.value();
-                    //if (mpContainer && sFuncName == u"custom")
-                    //{
-                    //    // TODO: store custom formula tokenarrays somewhere
-                    //    ScFormulaCell* pFC = 
mpContainer->GetDocument().GetFormulaCell(
-                    //        ScAddress(rSubTotalParam.nCol1 + i, 
rSubTotalParam.nRow2, nTable));
-                    //    if (pFC)
-                    //    {
-                    //        std::unique_ptr<ScTokenArray> pTokenArray = 
pFC->GetCode()->Clone();
-                    //    }
-                    //}
-                    pFunctions[nCheck] = 
ScDBData::GetSubTotalFuncFromString(sFuncName);
-                    nCheck++;
+                    ScSubTotalFunc eSubType = 
GetSubTotalFuncFromString(sFuncName);
+                    if (eSubType != SUBTOTAL_FUNC_NONE)
+                    {
+                        std::unique_ptr<ScTokenArray> pArr(new 
ScTokenArray(rDoc));
+                        pArr->AddOpCode(ocSubTotal);
+                        pArr->AddOpCode(ocOpen);
+                        pArr->AddDouble(static_cast<double>(eSubType));
+                        pArr->AddOpCode(ocSep);
+                        // Table refs structure
+                        pArr->AddTableRef(GetIndex());
+                        pArr->AddOpCode(ocTableRefOpen);
+                        ScSingleRefData aSingleRef;
+                        aSingleRef.InitAddress(nCol, rSubTotalParam.nRow1, 
nTable);
+                        pArr->AddSingleReference(aSingleRef);
+                        pArr->AddOpCode(ocTableRefClose);
+                        // Table refs structure end
+                        pArr->AddOpCode(ocClose);
+                        pArr->AddOpCode(ocStop);
+                        // Store
+                        vColFuncs.push_back(std::make_pair(nCol, 
std::move(pArr)));
+                    }
                 }
             }
-            rSubTotalParam.SetSubTotals(static_cast<sal_uInt16>(0), // group 
number
-                                        pSubTotals.get(), pFunctions.get(),
-                                        nTotalsCount); // number of array 
elements
+            nCol++;
         }
+        rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), vColLabels, 
vColLabels.size());
+        rSubTotalParam.SetCustFuncs(static_cast<sal_uInt16>(0), vColFuncs, 
vColFuncs.size());
+    }
+}
 
-        // how many col we have totals Row Label
-        size_t nLabelsCount = std::count_if(
-            GetTableColumnAttributes().begin(), 
GetTableColumnAttributes().end(),
-            [](const TableColumnAttributes& attr) { return 
attr.maTotalsRowLabel.has_value(); });
-        if (nLabelsCount > 0)
-        {
-            std::unique_ptr<OUString[]> pLabels;
-            std::unique_ptr<SCCOL[]> pSubLabels;
-            pLabels.reset(new OUString[nLabelsCount]);
-            pSubLabels.reset(new SCCOL[nLabelsCount]);
+void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const
+{
+    rSubTotalParam.bDoSort = false;
+    rSubTotalParam.bGroupedBy = false;
+    rSubTotalParam.aGroups[0].nField = rSubTotalParam.nCol1; // which column 
we add 'Summary'
 
-            for (size_t i = 0, nCheck = 0; i < nEntryCount; i++)
+    const size_t nEntryCount = rSubTotalParam.nCol2 - rSubTotalParam.nCol1 + 
1; // col count
+    if (nEntryCount > 0 && mpContainer)
+    {
+        ScDocument& rDoc = mpContainer->GetDocument();
+        ScHorizontalCellIterator aIter(rDoc, nTable, rSubTotalParam.nCol1, 
rSubTotalParam.nRow2,
+                                       rSubTotalParam.nCol2,
+                                       rSubTotalParam.nRow2); // Total row only
+        ScRefCellValue* pCell;
+        SCCOL nCol = rSubTotalParam.nCol1 - 1;
+        SCROW nRow;
+        std::vector<std::pair<SCCOL, OUString>> vColLabels;
+        std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>> vColFuncs;
+        while ((pCell = aIter.GetNext(nCol, nRow)) != nullptr)
+        {
+            if (pCell->getType() != CELLTYPE_FORMULA)
             {
-                if (GetTableColumnAttributes().size() <= i)
-                {
-                    SAL_WARN("sc.core",
-                             "ScDBData::CreateSubTotalParam - column 
attributes size mismatch");
-                    break;
-                }
-                if (GetTableColumnAttributes()[i].maTotalsRowLabel.has_value())
+                OUString aStr = pCell->getString(&rDoc);
+                if (!aStr.isEmpty())
+                    vColLabels.push_back(std::make_pair(nCol, 
std::move(aStr)));
+            }
+            else
+            {
+                if (ScFormulaCell* pFC = pCell->getFormula())
                 {
-                    pSubLabels[nCheck] = rSubTotalParam.nCol1 + i;
-                    pLabels[nCheck] = 
GetTableColumnAttributes()[i].maTotalsRowLabel.value();
-                    nCheck++;
+                    std::unique_ptr<ScTokenArray> pTokens = 
pFC->GetCode()->Clone();
+                    if (pTokens)
+                    {
+                        vColFuncs.push_back(std::make_pair(nCol, 
std::move(pTokens)));
+                    }
                 }
             }
-            rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), // group 
number
-                                        pSubLabels.get(), pLabels.get(),
-                                        nLabelsCount); // number of array 
elements
         }
+        rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), vColLabels, 
vColLabels.size());
+        rSubTotalParam.SetCustFuncs(static_cast<sal_uInt16>(0), vColFuncs, 
vColFuncs.size());
     }
 }
 
@@ -881,7 +915,6 @@ void ScDBData::UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos)
                 aRange.aEnd.Row());
         // Do not use SetTableColumnNames() because that resets 
mbTableColumnNamesDirty.
         maTableColumnNames = aNames;
-        maTableColumnAttributes.resize(aNames.size());
         mbTableColumnNamesDirty = bTableColumnNamesDirty;
     }
 
@@ -914,7 +947,7 @@ bool ScDBData::UpdateReference(const ScDocument* pDoc, 
UpdateRefMode eUpdateRefM
     if (bDoUpdate && eRet != UR_INVALID)
     {
         // MoveTo() invalidates column names via SetArea(); adjust, remember 
and set new.
-        AdjustTableColumnAttributes( eUpdateRefMode, nDx, nCol1, nOldCol1, 
nOldCol2, theCol1, theCol2);
+        AdjustTableColumnNames( eUpdateRefMode, nDx, nCol1, nOldCol1, 
nOldCol2, theCol1, theCol2);
         ::std::vector<OUString> aNames( maTableColumnNames);
         bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
         // tdf#48025, tdf#141946: update the column index of the filter 
criteria,
@@ -925,7 +958,6 @@ bool ScDBData::UpdateReference(const ScDocument* pDoc, 
UpdateRefMode eUpdateRefM
             MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
         // Do not use SetTableColumnNames() because that resets 
mbTableColumnNamesDirty.
         maTableColumnNames = aNames;
-        maTableColumnAttributes.resize(aNames.size());
         mbTableColumnNamesDirty = bTableColumnNamesDirty;
     }
 
@@ -1009,12 +1041,7 @@ void ScDBData::SetTableColumnNames( ::std::vector< 
OUString >&& rNames )
     mbTableColumnNamesDirty = false;
 }
 
-void ScDBData::SetTableColumnAttributes( ::std::vector< TableColumnAttributes 
>&& rAttributes )
-{
-    maTableColumnAttributes = std::move(rAttributes);
-}
-
-void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, 
SCCOL nDx, SCCOL nCol1,
+void ScDBData::AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL 
nDx, SCCOL nCol1,
         SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 )
 {
     if (maTableColumnNames.empty())
@@ -1026,7 +1053,6 @@ void ScDBData::AdjustTableColumnAttributes( UpdateRefMode 
eUpdateRefMode, SCCOL
         return;     // not moved or entirely moved, nothing to do
 
     ::std::vector<OUString> aNewNames;
-    ::std::vector<TableColumnAttributes> aNewAttributes;
     if (eUpdateRefMode == URM_INSDEL)
     {
         if (nDx > 0)
@@ -1043,26 +1069,22 @@ void ScDBData::AdjustTableColumnAttributes( 
UpdateRefMode eUpdateRefMode, SCCOL
             if (nDx > 0)
                 n += nDx;
             aNewNames.resize(n);
-            aNewAttributes.resize(n);
             // Copy head.
             for (size_t i = 0; i < nHead; ++i)
             {
                 aNewNames[i] = maTableColumnNames[i];
-                aNewAttributes[i] = maTableColumnAttributes[i];
             }
             // Copy tail, inserted middle range, if any, stays empty.
             for (size_t i = n - nTail, j = maTableColumnNames.size() - nTail; 
i < n; ++i, ++j)
             {
                 aNewNames[i] = maTableColumnNames[j];
-                aNewAttributes[i] = maTableColumnAttributes[j];
             }
         }
     } // else   empty aNewNames invalidates names/offsets
 
     SAL_WARN_IF( !maTableColumnNames.empty() && aNewNames.empty(),
-            "sc.core", "ScDBData::AdjustTableColumnAttributes - invalidating 
column attributes/offsets");
+            "sc.core", "ScDBData::AdjustTableColumnNames - invalidating column 
names/offsets");
     aNewNames.swap( maTableColumnNames);
-    aNewAttributes.swap(maTableColumnAttributes);
     if (maTableColumnNames.empty())
         mbTableColumnNamesDirty = true;
     if (mbTableColumnNamesDirty)
@@ -1198,7 +1220,6 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
     }
 
     aNewNames.swap( maTableColumnNames);
-    maTableColumnAttributes.resize(maTableColumnNames.size());
     mbTableColumnNamesDirty = false;
 }
 
diff --git a/sc/source/filter/excel/xedbdata.cxx 
b/sc/source/filter/excel/xedbdata.cxx
index 97fa10cf62b4..1283082fe13c 100644
--- a/sc/source/filter/excel/xedbdata.cxx
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -230,7 +230,6 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
     }
 
     const std::vector< OUString >& rColNames = rData.GetTableColumnNames();
-    const std::vector< TableColumnAttributes >& rColAttributes = 
rData.GetTableColumnAttributes();
     const std::vector< TableColumnModel >& rTableColumnModel = 
rData.GetTableColumnModel();
     if (!rColNames.empty())
     {
@@ -260,9 +259,9 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
             pTableStrm->startElement( XML_tableColumn,
                     XML_id, OString::number(i+1),
                     XML_uniqueName, uniqueName,
-                    XML_name, rColNames[i].toUtf8(),
-                    XML_totalsRowLabel, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsRowLabel : std::nullopt),
-                    XML_totalsRowFunction, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsFunction : std::nullopt)
+                    XML_name, rColNames[i].toUtf8()
+                    // XML_totalsRowLabel, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsRowLabel : std::nullopt),
+                    // XML_totalsRowFunction, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsFunction : std::nullopt)
                     // OOXTODO: XML_dataCellStyle, ...,
                     // OOXTODO: XML_dataDxfId, ...,
                     // OOXTODO: XML_headerRowCellStyle, ...,
diff --git a/sc/source/filter/inc/tablecolumnsbuffer.hxx 
b/sc/source/filter/inc/tablecolumnsbuffer.hxx
index d18d9dcab957..8abc3915afb6 100644
--- a/sc/source/filter/inc/tablecolumnsbuffer.hxx
+++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx
@@ -26,7 +26,6 @@
 
 namespace oox { class AttributeList; }
 namespace oox { class SequenceInputStream; }
-class ScDBData;
 
 namespace oox::xls {
 
@@ -43,20 +42,29 @@ public:
     void                importTableColumn( SequenceInputStream& rStrm );
     /** Gets the name of this column. */
     const OUString&     getName() const;
-    /** Gets the attributes of this column. */
-    const TableColumnAttributes& getColumnAttributes() const;
     /** Imports XML column properties for the xmlColumnPr element. */
     void                importXmlColumnPr(const AttributeList& rAttribs);
     /** Returns access to the table column model data. */
     TableColumnModel& getModel() { return maModel; }
+    /** Gets the Total Row Label of this column. */
+    const std::optional<OUString>& getColumnRowLabel() const;
+    /** Gets the Subtotal function of this column. */
+    const std::optional<OUString>& getColumnSubTotal() const;
+    /** Gets the Custom function of this column. */
+    const std::optional<OUString>& getColumnFunction() const;
+    /** Sets the function of this column. */
+    void                setFunc( const OUString& rChars );
 
 private:
     OUString            maName;
     sal_Int32           mnId;
     sal_Int32           mnDataDxfId;
-    TableColumnAttributes maColumnAttributes;
 
     TableColumnModel    maModel;
+
+    std::optional<OUString> maRowLabel = std::nullopt;
+    std::optional<OUString> maSubTotal = std::nullopt;
+    std::optional<OUString> maFunction = std::nullopt;
 };
 
 class TableColumns : public WorkbookHelper
diff --git a/sc/source/filter/inc/tablecolumnscontext.hxx 
b/sc/source/filter/inc/tablecolumnscontext.hxx
index 1f5a5ba046b5..21d123713f9d 100644
--- a/sc/source/filter/inc/tablecolumnscontext.hxx
+++ b/sc/source/filter/inc/tablecolumnscontext.hxx
@@ -33,6 +33,7 @@ public:
 
 protected:
     virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 
nElement, const AttributeList& rAttribs ) override;
+    virtual void        onCharacters( const rtl::OUString& rChars ) override;
     virtual void        onStartElement( const AttributeList& rAttribs ) 
override;
 
     virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 
nRecId, SequenceInputStream& rStrm ) override;
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx 
b/sc/source/filter/oox/tablecolumnsbuffer.cxx
index 81f46a43496d..0d0b6e0a75be 100644
--- a/sc/source/filter/oox/tablecolumnsbuffer.cxx
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -20,8 +20,10 @@
 #include <tablecolumnsbuffer.hxx>
 
 #include <sal/log.hxx>
+#include <formula/grammar.hxx>
 #include <oox/helper/attributelist.hxx>
 #include <oox/token/tokens.hxx>
+#include <dbdata.hxx>
 #include <subtotalparam.hxx>
 
 XmlColumnPrModel::XmlColumnPrModel() :
@@ -57,9 +59,9 @@ void TableColumn::importTableColumn( const AttributeList& 
rAttribs )
     maModel.maUniqueName = rAttribs.getXString( XML_uniqueName, OUString() );
     mnDataDxfId = rAttribs.getInteger( XML_dataDxfId, -1 );
     if ( rAttribs.hasAttribute(XML_totalsRowLabel ) )
-        maColumnAttributes.maTotalsRowLabel = rAttribs.getStringDefaulted( 
XML_totalsRowLabel );
+        maRowLabel = rAttribs.getStringDefaulted(XML_totalsRowLabel);
     if ( rAttribs.hasAttribute( XML_totalsRowFunction ) )
-        maColumnAttributes.maTotalsFunction = rAttribs.getStringDefaulted( 
XML_totalsRowFunction );
+        maSubTotal = rAttribs.getStringDefaulted(XML_totalsRowFunction);
 }
 
 void TableColumn::importTableColumn( SequenceInputStream& /*rStrm*/ )
@@ -73,9 +75,24 @@ const OUString& TableColumn::getName() const
     return maName;
 }
 
-const TableColumnAttributes& TableColumn::getColumnAttributes() const
+const std::optional<OUString>& TableColumn::getColumnRowLabel() const
 {
-    return maColumnAttributes;
+    return maRowLabel;
+}
+
+const std::optional<OUString>& TableColumn::getColumnSubTotal() const
+{
+    return maSubTotal;
+}
+
+const std::optional<OUString>& TableColumn::getColumnFunction() const
+{
+    return maFunction;
+}
+
+void TableColumn::setFunc( const OUString& rChars )
+{
+    maFunction = rChars;
 }
 
 void TableColumn::importXmlColumnPr(const AttributeList& rAttribs)
@@ -120,26 +137,37 @@ bool TableColumns::finalizeImport( ScDBData* pDBData )
     {
         /* TODO: use svl::SharedString for names */
         ::std::vector< OUString > aNames( maTableColumnVector.size());
-        ::std::vector< TableColumnAttributes > aAttributesVector( 
maTableColumnVector.size() );
+        ::std::vector< TableColumnAttributes > aAttributes( 
maTableColumnVector.size() );
         size_t i = 0;
+        bool hasAnySetValue = false;
         for (const auto& rxTableColumn : maTableColumnVector)
         {
             aNames[i] = rxTableColumn->getName();
-            aAttributesVector[i] = rxTableColumn->getColumnAttributes();
             pDBData->SetTableColumnModel( rxTableColumn->getModel() );
+            aAttributes[i].maTotalsRowLabel = 
rxTableColumn->getColumnRowLabel();
+            aAttributes[i].maTotalsFunction = 
rxTableColumn->getColumnSubTotal();
+            aAttributes[i].maCustomFunction = 
rxTableColumn->getColumnFunction();
+
+            if (!hasAnySetValue
+                && (aAttributes[i].maTotalsRowLabel.has_value()
+                    || aAttributes[i].maTotalsFunction.has_value()
+                    || aAttributes[i].maCustomFunction.has_value()))
+            {
+                hasAnySetValue = true;
+            }
+
             ++i;
         }
         pDBData->SetTableColumnNames( std::move(aNames) );
-        pDBData->SetTableColumnAttributes( std::move(aAttributesVector) );
-        // set subtotal parameters for columns
-        if (pDBData->HasTotals())
+
+        // Import subtotal parameters for columns
+        if (hasAnySetValue && !pDBData->HasTotals())
         {
             ScSubTotalParam aSubTotalParam;
             pDBData->GetSubTotalParam(aSubTotalParam);
             aSubTotalParam.bHasHeader = pDBData->HasHeader();
-            aSubTotalParam.bRemoveOnly = false;
-            aSubTotalParam.bReplace = false;
-            pDBData->CreateSubTotalParam(aSubTotalParam);
+            pDBData->ImportSubTotalParam(aSubTotalParam, aAttributes,
+                                         formula::FormulaGrammar::GRAM_OOXML);
             pDBData->SetSubTotalParam(aSubTotalParam);
         }
         return true;
diff --git a/sc/source/filter/oox/tablecolumnscontext.cxx 
b/sc/source/filter/oox/tablecolumnscontext.cxx
index 1129bcd22f7d..e1115c41a375 100644
--- a/sc/source/filter/oox/tablecolumnscontext.cxx
+++ b/sc/source/filter/oox/tablecolumnscontext.cxx
@@ -20,6 +20,7 @@
 #include <tablecolumnscontext.hxx>
 
 #include <tablecolumnsbuffer.hxx>
+#include <oox/helper/attributelist.hxx>
 #include <oox/token/namespaces.hxx>
 
 namespace oox::xls {
@@ -32,20 +33,38 @@ TableColumnContext::TableColumnContext( 
WorksheetContextBase& rParent, TableColu
 {
 }
 
+void TableColumnContext::onCharacters( const rtl::OUString& rChars )
+{
+    switch( getCurrentElement() )
+    {
+        case XLS_TOKEN( totalsRowFormula ):
+            mrTableColumn.setFunc(rChars);
+        break;
+    }
+}
+
 ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 nElement, 
const AttributeList& rAttribs )
 {
     switch (nElement)
     {
         case XLS_TOKEN(xmlColumnPr):
             mrTableColumn.importXmlColumnPr( rAttribs );
-            break;
+        break;
+        case XLS_TOKEN(totalsRowFormula):
+        {
+            if (getCurrentElement() == XLS_TOKEN(tableColumn) &&
+                rAttribs.getToken(XML_t, XML_normal) != XML_TOKEN_INVALID)
+                return this;
+        }
+        break;
     }
     return nullptr;
 }
 
 void TableColumnContext::onStartElement( const AttributeList& rAttribs )
 {
-    mrTableColumn.importTableColumn( rAttribs );
+    if (getCurrentElement() == XLS_TOKEN(tableColumn))
+        mrTableColumn.importTableColumn( rAttribs );
 }
 
 ContextHandlerRef TableColumnContext::onCreateRecordContext( sal_Int32 
/*nRecId*/, SequenceInputStream& /*rStrm*/ )
diff --git a/sc/source/ui/docshell/dbdocfun.cxx 
b/sc/source/ui/docshell/dbdocfun.cxx
index 24724a60da33..2f7a070a772e 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -829,14 +829,17 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& 
rQueryParam,
     weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
 
     bool bKeepSub = false;                          // repeat existing partial 
results?
+    bool bKeepTotals = false;
     if (rQueryParam.GetEntry(0).bDoQuery)           // not at cancellation
     {
         ScSubTotalParam aSubTotalParam;
         pDBData->GetSubTotalParam( aSubTotalParam );    // partial results 
exist?
 
-        if ((aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
-            || (pDBData->HasTotals() && pDBData->GetTableStyleInfo()))
+        if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
             bKeepSub = true;
+
+        if (pDBData->HasTotals() && pDBData->GetTableStyleInfo())
+            bKeepTotals = true;
     }
 
     ScDocumentUniquePtr pUndoDoc;
@@ -902,7 +905,7 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& 
rQueryParam,
     }
 
     //  execute filtering on the document
-    SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub );
+    SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub, bKeepTotals );
     pDBData->CalcSaveFilteredCount( nCount );
     if (bCopy)
     {
@@ -1275,7 +1278,6 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const 
ScDBData& rNewData, const
 
     ScSubTotalParam aNewParam;
     rNewData.GetSubTotalParam(aNewParam); // end of range is being changed
-    // ScSubTotalParam aNewParam(rParam);
     ScDocumentUniquePtr pUndoDoc;
     std::unique_ptr<ScRangeName> pUndoRange;
     std::unique_ptr<ScDBCollection> pUndoDB;
@@ -1310,7 +1312,7 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const 
ScDBData& rNewData, const
     bool bSuccess = true;
     if (bDo)
     {
-        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex());
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
         rDoc.SetDrawPageSize(nTab);
     }
     ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx
index 2eb32f19f755..8428509c8aa1 100644
--- a/sc/source/ui/view/dbfunc3.cxx
+++ b/sc/source/ui/view/dbfunc3.cxx
@@ -671,7 +671,7 @@ void ScDBFunc::DoTableSubTotals( const ScDBData& rNewData, 
const ScSubTotalParam
     bool bSuccess = true;
     if (bDo)
     {
-        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex());
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
     }
     ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
                         nTab);
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 1520d4ec8385..e71c3e4b3f83 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -2501,11 +2501,9 @@ void ScGridWindow::MouseButtonUp( const MouseEvent& 
rMEvt )
                 ScSubTotalParam aSubTotalParam;
                 pDBData->GetSubTotalParam(aSubTotalParam);
                 aSubTotalParam.bHasHeader = aNewDBData.HasHeader();
-                if (!aNewDBData.HasSubTotalParam())
-                {
-                    pDBData->CreateSubTotalParam(aSubTotalParam);
-                    aNewDBData.SetSubTotalParam(aSubTotalParam);
-                }
+                // store current subtotal settings
+                pDBData->CreateSubTotalParam(aSubTotalParam);
+                aNewDBData.SetSubTotalParam(aSubTotalParam);
                 // add/replace total row
                 aSubTotalParam.bRemoveOnly = false;
                 aSubTotalParam.bReplace = true;
diff --git a/sc/source/ui/view/tableshell.cxx b/sc/source/ui/view/tableshell.cxx
index 9b3769625b8d..bb35a0d98ba7 100644
--- a/sc/source/ui/view/tableshell.cxx
+++ b/sc/source/ui/view/tableshell.cxx
@@ -99,14 +99,12 @@ void ScTableShell::ExecuteDatabaseSettings(SfxRequest& rReq)
                         ScSubTotalParam aSubTotalParam;
                         aNewDBData.GetSubTotalParam(aSubTotalParam);
                         aSubTotalParam.bHasHeader = aNewDBData.HasHeader();
-                        if (!aNewDBData.HasSubTotalParam())
-                        {
-                            pDBData->CreateSubTotalParam(aSubTotalParam);
-                            aNewDBData.SetSubTotalParam(aSubTotalParam);
-                        }
 
                         if (!aNewDBData.HasTotals())
                         {
+                            // store current subtotal settings
+                            pDBData->CreateSubTotalParam(aSubTotalParam);
+                            aNewDBData.SetSubTotalParam(aSubTotalParam);
                             // remove total row
                             aSubTotalParam.bRemoveOnly = true;
                             aSubTotalParam.bReplace = true;
commit bcfc5fccdf2d6d355d9369a00de04edb7333d6aa
Author:     Balazs Varga <[email protected]>
AuthorDate: Mon Oct 27 12:20:48 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Mon Nov 17 19:02:56 2025 +0100

    Do not extend DbDataRange automatically for TableStyles
    
    when we have Total Rows. Also do not set bActive for groups
    in SubtotalParam otherwise the original Subtotal will run.
    Use TableRefs[ColumnName] for Total functions.
    
    Change-Id: Id95aa3ab58a7de7c7640a8e7d724df089fb66065
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193204
    Tested-by: Balazs Varga <[email protected]>
    Reviewed-by: Balazs Varga <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193680
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Andras Timar <[email protected]>

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 90bbad32c232..7d7caeac88b6 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1213,7 +1213,7 @@ public:
     bool            DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     void            RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     // Table SubTotals
-    bool            DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
+    bool            DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
sal_uInt16 nIndex );
     void            RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
const ScSubTotalParam& rOldParam );
 
     bool            TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& 
rParam );
diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc
index 74f21a7efad7..b124996be1c3 100644
--- a/sc/inc/globstr.hrc
+++ b/sc/inc/globstr.hrc
@@ -208,6 +208,7 @@
 #define STR_TABLE_GRAND_STDDEV                  NC_("STR_TABLE_GRAND_STDDEV", 
"Grand StdDev")
 #define STR_TABLE_GRAND_SUM                     NC_("STR_TABLE_GRAND_SUM", 
"Grand Sum")
 #define STR_TABLE_GRAND_VAR                     NC_("STR_TABLE_GRAND_VAR", 
"Grand Var")
+#define STR_TABLE_TOTAL                         NC_("STR_TABLE_TOTAL", 
"Summary")
 #define STR_NOCHARTATCURSOR                     NC_("STR_NOCHARTATCURSOR", "No 
chart found at this position.")
 #define STR_PIVOT_NOTFOUND                      NC_("STR_PIVOT_NOTFOUND", "No 
pivot table found at this position.")
 #define STR_EMPTYDATA                           NC_("STR_EMPTYDATA", "(empty)")
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 591d2112e747..a361d4f5ea0c 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -330,7 +330,7 @@ public:
     void        RemoveSubTotals( ScSubTotalParam& rParam );
     void        RemoveSimpleSubTotals( ScSubTotalParam& rParam, const 
ScSubTotalParam& rOldParam );
     bool        DoSubTotals( ScSubTotalParam& rParam );
-    bool        DoSimpleSubTotals( ScSubTotalParam& rParam );
+    bool        DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex 
);
 
     const ScSheetEvents* GetSheetEvents() const              { return 
pSheetEvents.get(); }
     void        SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew );
diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index 90495d9446ba..08938553ea4f 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -114,6 +114,7 @@ public:
     SC_DLLPUBLIC formula::FormulaToken* AddDoubleReference( const 
ScComplexRefData& rRef );
     SC_DLLPUBLIC void      AddRangeName( sal_uInt16 n, sal_Int16 nSheet );
     formula::FormulaToken* AddDBRange( sal_uInt16 n );
+    SC_DLLPUBLIC formula::FormulaToken* AddTableRef( sal_uInt16 n );
     SC_DLLPUBLIC formula::FormulaToken* AddExternalName( sal_uInt16 nFileId, 
const svl::SharedString& rName );
     SC_DLLPUBLIC void AddExternalSingleReference( sal_uInt16 nFileId, const 
svl::SharedString& rTabName, const ScSingleRefData& rRef );
     SC_DLLPUBLIC formula::FormulaToken* AddExternalDoubleReference( sal_uInt16 
nFileId, const svl::SharedString& rTabName, const ScComplexRefData& rRef );
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
index 265ab5e6b392..244d2a95341d 100644
--- a/sc/source/core/data/documen3.cxx
+++ b/sc/source/core/data/documen3.cxx
@@ -793,10 +793,10 @@ void ScDocument::RemoveTableSubTotals( SCTAB nTab, 
ScSubTotalParam& rParam, cons
         pTable->RemoveSimpleSubTotals( rParam, rOldParam );
 }
 
-bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
sal_uInt16 nIndex )
 {
     ScTable* pTable = FetchTable(nTab);
-    return pTable && pTable->DoSimpleSubTotals(rParam);
+    return pTable && pTable->DoSimpleSubTotals(rParam, nIndex);
 }
 
 bool ScDocument::HasSubTotalCells( const ScRange& rRange )
@@ -1580,9 +1580,12 @@ void ScDocument::GetFilterEntries(
     ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, 
ScDBDataPortion::AREA);  //!??
     if (!pDBData)
         return;
-
-    pDBData->ExtendBackColorArea(*this);
-    pDBData->ExtendDataArea(*this);
+    // Do not extand DBArea automatically in case of Table Styles with Total 
row
+    if (!pDBData->HasTotals() || !pDBData->GetTableStyleInfo())
+    {
+        pDBData->ExtendBackColorArea(*this);
+        pDBData->ExtendDataArea(*this);
+    }
     SCTAB nAreaTab;
     SCCOL nStartCol;
     SCROW nStartRow;
@@ -1592,6 +1595,9 @@ void ScDocument::GetFilterEntries(
 
     if (pDBData->HasHeader())
         ++nStartRow;
+    // For Table Styles area, exclude total row
+    if (pDBData->HasTotals() && pDBData->GetTableStyleInfo())
+        --nEndRow;
 
     ScQueryParam aParam;
     pDBData->GetQueryParam( aParam );
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index f00614448a9f..ea9ddcffdc02 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -2302,7 +2302,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
     return bSpaceLeft;
 }
 
-bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam )
+bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex )
 {
     RowEntry aRowEntry;
     aRowEntry.nGroupNo = 0;
@@ -2330,30 +2330,27 @@ bool ScTable::DoSimpleSubTotals( ScSubTotalParam& 
rParam )
     }
     else
     {
-        SetString(group.nField, aRowEntry.nDestRow, nTab, u"Summary"_ustr);
+        SetString(group.nField, aRowEntry.nDestRow, nTab, 
ScResId(STR_TABLE_TOTAL));
     }
 
     // insert the formulas
     if (group.nSubTotals > 0)
     {
-        ScComplexRefData aRef;
-        aRef.InitFlags();
-        aRef.Ref1.SetAbsTab(nTab);
-        aRef.Ref2.SetAbsTab(nTab);
-
         for (SCCOL nResult = 0; nResult < group.nSubTotals; ++nResult)
         {
-            aRef.Ref1.SetAbsCol(group.col(nResult));
-            aRef.Ref1.SetAbsRow(aRowEntry.nFuncStart);
-            aRef.Ref2.SetAbsCol(group.col(nResult));
-            aRef.Ref2.SetAbsRow(aRowEntry.nFuncEnd);
-            // TODO: handle it with tablerefs: Table1[Column1]
             ScTokenArray aArr(rDocument);
             aArr.AddOpCode(ocSubTotal);
             aArr.AddOpCode(ocOpen);
             aArr.AddDouble(static_cast<double>(group.func(nResult)));
             aArr.AddOpCode(ocSep);
-            aArr.AddDoubleReference(aRef);
+            // Table refs structure
+            aArr.AddTableRef(nIndex);
+            aArr.AddOpCode(ocTableRefOpen);
+            ScSingleRefData aSingleRef;
+            aSingleRef.InitAddress(group.col(nResult), aRowEntry.nFuncStart - 
1, nTab);
+            aArr.AddSingleReference(aSingleRef);
+            aArr.AddOpCode(ocTableRefClose);
+            // Table refs structure end
             aArr.AddOpCode(ocClose);
             aArr.AddOpCode(ocStop);
             ScFormulaCell* pCell = new ScFormulaCell(
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 742a03628b30..0d00773be892 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -701,7 +701,6 @@ void ScDBData::CreateSubTotalParam(ScSubTotalParam& 
rSubTotalParam) const
 {
     rSubTotalParam.bDoSort = false;
     rSubTotalParam.bGroupedBy = false;
-    rSubTotalParam.aGroups[0].bActive = true;
     rSubTotalParam.aGroups[0].nField = rSubTotalParam.nCol1; // which column 
we add 'Summary'
 
     const size_t nEntryCount = rSubTotalParam.nCol2 - rSubTotalParam.nCol1 + 
1; // col count
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index d2d57372a91b..eb9ba6c1db50 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -2355,6 +2355,11 @@ FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n )
     return Add( new FormulaIndexToken( ocDBArea, n));
 }
 
+FormulaToken* ScTokenArray::AddTableRef( sal_uInt16 n )
+{
+    return Add( new ScTableRefToken(n, ScTableRefToken::TABLE));
+}
+
 FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const 
svl::SharedString& rName )
 {
     return Add( new ScExternalNameToken(nFileId, rName) );
diff --git a/sc/source/ui/docshell/dbdocfun.cxx 
b/sc/source/ui/docshell/dbdocfun.cxx
index 08fa659d2ce6..24724a60da33 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -834,7 +834,8 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& 
rQueryParam,
         ScSubTotalParam aSubTotalParam;
         pDBData->GetSubTotalParam( aSubTotalParam );    // partial results 
exist?
 
-        if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
+        if ((aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
+            || (pDBData->HasTotals() && pDBData->GetTableStyleInfo()))
             bKeepSub = true;
     }
 
@@ -1309,22 +1310,7 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const 
ScDBData& rNewData, const
     bool bSuccess = true;
     if (bDo)
     {
-        // sort
-        if (rParam.bDoSort)
-        {
-            pDBData->SetArea(nTab, aNewParam.nCol1, aNewParam.nRow1, 
aNewParam.nCol2,
-                             aNewParam.nRow2);
-
-            //  set partial result field to before the sorting
-            //  (Duplicates are omitted, so can be called again)
-
-            ScSortParam aOldSort;
-            pDBData->GetSortParam(aOldSort);
-            ScSortParam aSortParam(aNewParam, aOldSort);
-            Sort(nTab, aSortParam, false, false, bApi);
-        }
-
-        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex());
         rDoc.SetDrawPageSize(nTab);
     }
     ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx
index 8428509c8aa1..2eb32f19f755 100644
--- a/sc/source/ui/view/dbfunc3.cxx
+++ b/sc/source/ui/view/dbfunc3.cxx
@@ -671,7 +671,7 @@ void ScDBFunc::DoTableSubTotals( const ScDBData& rNewData, 
const ScSubTotalParam
     bool bSuccess = true;
     if (bDo)
     {
-        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex());
     }
     ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
                         nTab);

Reply via email to