cache mdds access in ScTable::ValidQuery() (tdf#80853)

Once more, mdds always searches from the start of the container,
so iterating is quadratic.

Change-Id: I8f8f3b5aad5c3342a10c21df3ad2d0d3fcaea8ad
Reviewed-on: https://gerrit.libreoffice.org/72368
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index f55bc63..9a4d22a 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -218,6 +218,8 @@ public:
                          bool bConsiderCellDrawObjects=false) const;
    bool        HasDataAt(sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow, bool bConsiderCellNotes=false,
                          bool bConsiderCellDrawObjects=false) const;
    bool        HasDataAt(sc::ColumnBlockPosition& rBlockPos, SCROW nRow, bool bConsiderCellNotes=false,
                          bool bConsiderCellDrawObjects=false);
    bool        HasVisibleDataAt(SCROW nRow) const;
    SCROW       GetFirstDataPos() const;
    SCROW       GetLastDataPos() const;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 547d993..59dc2bb 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -64,6 +64,7 @@ class ColumnSpanSet;
class RangeColumnSpanSet;
class ColumnSet;
struct ColumnBlockPosition;
class TableColumnBlockPositionSet;
struct RefUpdateContext;
struct RefUpdateInsertTabContext;
struct RefUpdateDeleteTabContext;
@@ -930,7 +931,8 @@ public:

    bool ValidQuery(
        SCROW nRow, const ScQueryParam& rQueryParam, const ScRefCellValue* pCell = nullptr,
        bool* pbTestEqualCondition = nullptr, const ScInterpreterContext* pContext = nullptr);
        bool* pbTestEqualCondition = nullptr, const ScInterpreterContext* pContext = nullptr,
        sc::TableColumnBlockPositionSet* pBlockPos = nullptr );
    void        TopTenQuery( ScQueryParam& );
    SCSIZE      Query(const ScQueryParam& rQueryParam, bool bKeepSub);
    bool        CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam);
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 2568041..d8e2646 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -3100,6 +3100,22 @@ bool ScColumn::HasDataAt(sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
    return aPos.first->type != sc::element_type_empty;
}

bool ScColumn::HasDataAt(sc::ColumnBlockPosition& rBlockPos, SCROW nRow,
                         bool bConsiderCellNotes, bool bConsiderCellDrawObjects)
{
    if (bConsiderCellNotes && !IsNotesEmptyBlock(nRow, nRow))
        return true;

    if (bConsiderCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow))
        return true;

    std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
    if (aPos.first == maCells.end())
        return false;
    rBlockPos.miCellPos = aPos.first; // Store this for next call.
    return aPos.first->type != sc::element_type_empty;
}

bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
{
    if (pAttrArray && rCol.pAttrArray)
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index d04d704..da1e5e9 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -2729,7 +2729,7 @@ public:

bool ScTable::ValidQuery(
    SCROW nRow, const ScQueryParam& rParam, const ScRefCellValue* pCell, bool* pbTestEqualCondition,
    const ScInterpreterContext* pContext)
    const ScInterpreterContext* pContext, sc::TableColumnBlockPositionSet* pBlockPos)
{
    if (!rParam.GetEntry(0).bDoQuery)
        return true;
@@ -2753,19 +2753,36 @@ bool ScTable::ValidQuery(

        // We can only handle one single direct query passed as a known pCell,
        // subsequent queries have to obtain the cell.
        ScRefCellValue aCell( (pCell && it == itBeg) ? *pCell : GetCellValue(nCol, nRow));
        ScRefCellValue aCell;
        if(pCell && it == itBeg)
            aCell = *pCell;
        else if( pBlockPos )
        {   // hinted mdds access
            ScColumn* column = FetchColumn(nCol);
            aCell = column->GetCellValue(*pBlockPos->getBlockPosition( nCol ), nRow);
        }
        else
            aCell = GetCellValue(nCol, nRow);

        std::pair<bool,bool> aRes(false, false);

        const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
        if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
        {
            bool hasData;
            if( pBlockPos )
            {
                ScColumn* column = FetchColumn(rEntry.nField);
                hasData = column->HasDataAt(*pBlockPos->getBlockPosition(rEntry.nField), nRow);
            }
            else
                hasData = aCol[rEntry.nField].HasDataAt(nRow);
            if (rEntry.IsQueryByEmpty())
                aRes.first = !aCol[rEntry.nField].HasDataAt(nRow);
                aRes.first = !hasData;
            else
            {
                assert(rEntry.IsQueryByNonEmpty());
                aRes.first = aCol[rEntry.nField].HasDataAt(nRow);
                aRes.first = hasData;
            }
        }
        else
@@ -3056,11 +3073,13 @@ SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
                            aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
    }

    sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access

    SCROW nRealRow2 = aParam.nRow2;
    for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
    {
        bool bResult;                                   // Filter result
        bool bValid = ValidQuery(j, aParam);
        bool bValid = ValidQuery(j, aParam, nullptr, nullptr, nullptr, &blockPos);
        if (!bValid && bKeepSub)                        // Keep subtotals
        {
            for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)