sc/inc/column.hxx                 |    3 
 sc/inc/document.hxx               |    3 
 sc/inc/queryiter.hxx              |  105 ++++++++-----
 sc/inc/table.hxx                  |    3 
 sc/inc/types.hxx                  |    6 
 sc/source/core/data/queryiter.cxx |  290 ++++++++++++++------------------------
 sc/source/core/tool/interpr1.cxx  |    2 
 7 files changed, 190 insertions(+), 222 deletions(-)

New commits:
commit d5986d1c5f639f21204412ebe4a2a9487088b2f3
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Apr 22 13:33:05 2022 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Fri Apr 22 18:16:48 2022 +0200

    do not duplicate code for ScCountIfCellIterator
    
    d468958331f36310d11265ba55d7c27366ab58ab improved COUNTIF performance
    by copy&pasting the generic query iterator and then basically removing
    what was not necessary. Which is in general a good way to improve
    the performance, except for the copy&paste part, as the code has
    already started to diverge (e.g. fc3b904b671a71266db2e8b30cbeeef4f79).
    
    So instead make the shared code into a template and reuse that from
    specific code. This has exactly the same performance as the copy&paste
    way.
    
    Change-Id: I168319a1f4273bafc8c0742a114dafbf433968bb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133324
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 812a2e6b5abc..ed9ce2e0c77c 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -208,8 +208,9 @@ friend class ScTable;
 friend class ScValueIterator;
 friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
+template< ScQueryCellIteratorType >
+friend class ScQueryCellIteratorBase;
 friend class ScQueryCellIterator;
-friend class ScCountIfCellIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
 friend class ScHorizontalCellIterator;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 3a19ffa02ffb..5fb616b0076b 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -323,8 +323,9 @@ friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
+template< ScQueryCellIteratorType >
+friend class ScQueryCellIteratorBase;
 friend class ScQueryCellIterator;
-friend class ScCountIfCellIterator;
 friend class ScHorizontalCellIterator;
 friend class ScHorizontalAttrIterator;
 friend class ScDocAttrIterator;
diff --git a/sc/inc/queryiter.hxx b/sc/inc/queryiter.hxx
index 70b8daaf4153..ae7b8066c57b 100644
--- a/sc/inc/queryiter.hxx
+++ b/sc/inc/queryiter.hxx
@@ -24,9 +24,24 @@
 #include "scdllapi.h"
 #include "queryparam.hxx"
 #include "mtvelements.hxx"
+#include "types.hxx"
 
-class ScQueryCellIterator           // walk through all non-empty cells in an 
area
+// Query-related iterators. There is one template class ScQueryCellIteratorBase
+// that implements most of the shared functionality, specific parts are done
+// by specializing the templates and then subclassing as the actual class to 
use.
+
+// Specific data should be in ScQueryCellIteratorSpecific (otherwise adding 
data
+// members here would mean specializing the entire ScQueryCellIteratorBase).
+template< ScQueryCellIteratorType iteratorType >
+class ScQueryCellIteratorSpecific
+{
+};
+
+// Shared code for query-based iterators.
+template< ScQueryCellIteratorType iteratorType >
+class ScQueryCellIteratorBase : public ScQueryCellIteratorSpecific< 
iteratorType >
 {
+protected:
     enum StopOnMismatchBits
     {
         nStopOnMismatchDisabled = 0x00,
@@ -61,28 +76,20 @@ class ScQueryCellIterator           // walk through all 
non-empty cells in an ar
     void InitPos();
     void IncPos();
     void IncBlock();
-    bool GetThis();
 
-                    /* Only works if no regular expression is involved, only
-                       searches for rows in one column, and only the first
-                       query entry is considered with simple conditions
-                       SC_LESS_EQUAL (sorted ascending) or SC_GREATER_EQUAL
-                       (sorted descending). Check these things before
-                       invocation! Delivers a starting point, continue with
-                       GetThis() and GetNext() afterwards. Introduced for
-                       FindEqualOrSortedLastInRange()
-                     */
-    bool BinarySearch();
+    // The actual query function. It will call HandleItemFound() for any 
matching type
+    // and return if HandleItemFound() returns true.
+    void PerformQuery();
+    bool HandleItemFound(); // not implemented, needs specialization
+
+    SCCOL           GetCol() const { return nCol; }
+    SCROW           GetRow() const { return nRow; }
 
 public:
-                    ScQueryCellIterator(ScDocument& rDocument, const 
ScInterpreterContext& rContext, SCTAB nTable,
-                                        const ScQueryParam& aParam, bool bMod);
+                    ScQueryCellIteratorBase(ScDocument& rDocument, const 
ScInterpreterContext& rContext, SCTAB nTable,
+                                            const ScQueryParam& aParam, bool 
bMod);
                                         // when !bMod, the QueryParam has to 
be filled
                                         // (bIsString)
-    bool GetFirst();
-    bool GetNext();
-    SCCOL           GetCol() const { return nCol; }
-    SCROW           GetRow() const { return nRow; }
 
                     // increments all Entry.nField, if column
                     // changes, for ScInterpreter ScHLookup()
@@ -121,6 +128,37 @@ public:
                         }
     bool            IsEqualConditionFulfilled() const
                         { return nTestEqualCondition == 
nTestEqualConditionFulfilled; }
+};
+
+template<>
+class ScQueryCellIteratorSpecific< ScQueryCellIteratorType::Generic >
+{
+protected:
+    bool getThisResult;
+};
+
+// The generic query iterator, used e.g. by VLOOKUP.
+class ScQueryCellIterator : public ScQueryCellIteratorBase< 
ScQueryCellIteratorType::Generic >
+{
+                    /* Only works if no regular expression is involved, only
+                       searches for rows in one column, and only the first
+                       query entry is considered with simple conditions
+                       SC_LESS_EQUAL (sorted ascending) or SC_GREATER_EQUAL
+                       (sorted descending). Check these things before
+                       invocation! Delivers a starting point, continue with
+                       GetThis() and GetNext() afterwards. Introduced for
+                       FindEqualOrSortedLastInRange()
+                     */
+    bool BinarySearch();
+
+    bool GetThis();
+
+public:
+    using ScQueryCellIteratorBase::ScQueryCellIteratorBase;
+    bool GetFirst();
+    bool GetNext();
+    using ScQueryCellIteratorBase::GetCol;
+    using ScQueryCellIteratorBase::GetRow;
 
                     /** In a range assumed to be sorted find either the last of
                         a sequence of equal entries or the last being less than
@@ -143,29 +181,20 @@ public:
     bool            FindEqualOrSortedLastInRange( SCCOL& nFoundCol, SCROW& 
nFoundRow );
 };
 
-// Used by ScInterpreter::ScCountIf.
-// Walk through all non-empty cells in an area.
-class ScCountIfCellIterator
-{
-    typedef sc::CellStoreType::const_position_type PositionType;
-    PositionType    maCurPos;
-    ScQueryParam    maParam;
-    ScDocument&     rDoc;
-    const ScInterpreterContext& mrContext;
-    SCTAB           nTab;
-    SCCOL           nCol;
-    SCROW           nRow;
 
-    /** Initialize position for new column. */
-    void            InitPos();
-    void            IncPos();
-    void            IncBlock();
-    void            AdvanceQueryParamEntryField();
+template<>
+class ScQueryCellIteratorSpecific< ScQueryCellIteratorType::CountIf >
+{
+protected:
+    sal_uInt64 countIfCount;
+};
 
+// Used by ScInterpreter::ScCountIf.
+class ScCountIfCellIterator : public ScQueryCellIteratorBase< 
ScQueryCellIteratorType::CountIf >
+{
 public:
-                    ScCountIfCellIterator(ScDocument& rDocument, const 
ScInterpreterContext& rContext, SCTAB nTable,
-                                        const ScQueryParam& aParam);
-    int             GetCount();
+    using ScQueryCellIteratorBase::ScQueryCellIteratorBase;
+    sal_uInt64 GetCount();
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 2e2d78360e73..7e2d8576a1ee 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -260,8 +260,9 @@ friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
+template< ScQueryCellIteratorType >
+friend class ScQueryCellIteratorBase;
 friend class ScQueryCellIterator;
-friend class ScCountIfCellIterator;
 friend class ScHorizontalCellIterator;
 friend class ScColumnTextWidthIterator;
 friend class ScDocumentImport;
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 3bb2db710f79..ef027058cec1 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -133,4 +133,10 @@ namespace o3tl{
     template<> struct typed_flags<sc::MatrixEdge> : 
o3tl::is_typed_flags<sc::MatrixEdge, 63> {};
 }
 
+enum class ScQueryCellIteratorType
+{
+  Generic,
+  CountIf
+};
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/queryiter.cxx 
b/sc/source/core/data/queryiter.cxx
index 27217b0a1eb2..9f0ada59a521 100644
--- a/sc/source/core/data/queryiter.cxx
+++ b/sc/source/core/data/queryiter.cxx
@@ -55,16 +55,17 @@
 #include <limits>
 #include <vector>
 
-ScQueryCellIterator::ScQueryCellIterator(ScDocument& rDocument, const 
ScInterpreterContext& rContext, SCTAB nTable,
-             const ScQueryParam& rParam, bool bMod ) :
-    maParam(rParam),
-    rDoc( rDocument ),
-    mrContext( rContext ),
-    nTab( nTable),
-    nStopOnMismatch( nStopOnMismatchDisabled ),
-    nTestEqualCondition( nTestEqualConditionDisabled ),
-    bAdvanceQuery( false ),
-    bIgnoreMismatchOnLeadingStrings( false )
+template< ScQueryCellIteratorType iteratorType >
+ScQueryCellIteratorBase< iteratorType >::ScQueryCellIteratorBase(ScDocument& 
rDocument,
+    const ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& 
rParam, bool bMod )
+    : maParam(rParam)
+    , rDoc( rDocument )
+    , mrContext( rContext )
+    , nTab( nTable)
+    , nStopOnMismatch( nStopOnMismatchDisabled )
+    , nTestEqualCondition( nTestEqualConditionDisabled )
+    , bAdvanceQuery( false )
+    , bIgnoreMismatchOnLeadingStrings( false )
 {
     nCol = maParam.nCol1;
     nRow = maParam.nRow1;
@@ -84,7 +85,8 @@ ScQueryCellIterator::ScQueryCellIterator(ScDocument& 
rDocument, const ScInterpre
     }
 }
 
-void ScQueryCellIterator::InitPos()
+template< ScQueryCellIteratorType iteratorType >
+void ScQueryCellIteratorBase< iteratorType >::InitPos()
 {
     nRow = maParam.nRow1;
     if (maParam.bHasHeader && maParam.bByRow)
@@ -93,7 +95,8 @@ void ScQueryCellIterator::InitPos()
     maCurPos = rCol.maCells.position(nRow);
 }
 
-void ScQueryCellIterator::IncPos()
+template< ScQueryCellIteratorType iteratorType >
+void ScQueryCellIteratorBase< iteratorType >::IncPos()
 {
     if (maCurPos.second + 1 < maCurPos.first->size)
     {
@@ -106,7 +109,8 @@ void ScQueryCellIterator::IncPos()
         IncBlock();
 }
 
-void ScQueryCellIterator::IncBlock()
+template< ScQueryCellIteratorType iteratorType >
+void ScQueryCellIteratorBase< iteratorType >::IncBlock()
 {
     ++maCurPos.first;
     maCurPos.second = 0;
@@ -114,12 +118,14 @@ void ScQueryCellIterator::IncBlock()
     nRow = maCurPos.first->position;
 }
 
-bool ScQueryCellIterator::GetThis()
+template< ScQueryCellIteratorType iteratorType >
+void ScQueryCellIteratorBase< iteratorType >::PerformQuery()
 {
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     const ScQueryEntry& rEntry = maParam.GetEntry(0);
     const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
 
+    const bool bSingleQueryItem = rEntry.GetQueryItems().size() == 1;
     SCCOLROW nFirstQueryField = rEntry.nField;
     bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings &&
         rItem.meType != ScQueryEntry::ByString;
@@ -130,6 +136,22 @@ bool ScQueryCellIterator::GetThis()
     bool bTestEqualCondition = false;
     ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], maParam, 
&mrContext,
         (nTestEqualCondition ? &bTestEqualCondition : nullptr));
+    if( iteratorType == ScQueryCellIteratorType::CountIf )
+    {
+        // These are not used for COUNTIF, so should not be set, make the 
compiler
+        // explicitly aware of it so that the relevant parts are optimized 
away.
+        assert( !bAllStringIgnore );
+        assert( !bIgnoreMismatchOnLeadingStrings );
+        assert( nStopOnMismatch == nStopOnMismatchDisabled );
+        assert( nTestEqualCondition == nTestEqualConditionDisabled );
+        bAllStringIgnore = false;
+        bIgnoreMismatchOnLeadingStrings = false;
+        nStopOnMismatch = nStopOnMismatchDisabled;
+        nTestEqualCondition = nTestEqualConditionDisabled;
+        // This one is always set.
+        assert( bAdvanceQuery );
+        bAdvanceQuery = true;
+    }
 
     ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
     while (true)
@@ -147,7 +169,7 @@ bool ScQueryCellIterator::GetThis()
             {
                 ++nCol;
                 if (nCol > maParam.nCol2 || nCol >= 
rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
-                    return false;
+                    return;
                 if ( bAdvanceQuery )
                 {
                     AdvanceQueryParamEntryField();
@@ -166,7 +188,7 @@ bool ScQueryCellIterator::GetThis()
 
         if (maCurPos.first->type == sc::element_type_empty)
         {
-            if (rItem.mbMatchEmpty && rEntry.GetQueryItems().size() == 1)
+            if (rItem.mbMatchEmpty && bSingleQueryItem)
             {
                 // This shortcut, instead of determining if any SC_OR query
                 // exists or this query is SC_AND'ed (which wouldn't make
@@ -177,7 +199,10 @@ bool ScQueryCellIterator::GetThis()
                 // XXX this would have to be reworked if other filters used it
                 // in different manners and evaluation would have to be done in
                 // ValidQuery().
-                return true;
+                if(HandleItemFound())
+                    return;
+                IncPos();
+                continue;
             }
             else
             {
@@ -197,7 +222,12 @@ bool ScQueryCellIterator::GetThis()
             {
                 if ( nTestEqualCondition && bTestEqualCondition )
                     nTestEqualCondition |= nTestEqualConditionMatched;
-                return !aCell.isEmpty(); // Found it!
+                if ( aCell.isEmpty())
+                    return;
+                if( HandleItemFound())
+                    return;
+                IncPos();
+                continue;
             }
             else if ( nStopOnMismatch )
             {
@@ -208,7 +238,7 @@ bool ScQueryCellIterator::GetThis()
                 {
                     nTestEqualCondition |= nTestEqualConditionMatched;
                     nStopOnMismatch |= nStopOnMismatchOccurred;
-                    return false;
+                    return;
                 }
                 bool bStop;
                 if (bFirstStringIgnore)
@@ -226,7 +256,7 @@ bool ScQueryCellIterator::GetThis()
                 if (bStop)
                 {
                     nStopOnMismatch |= nStopOnMismatchOccurred;
-                    return false;
+                    return;
                 }
             }
             else
@@ -236,25 +266,8 @@ bool ScQueryCellIterator::GetThis()
     }
 }
 
-bool ScQueryCellIterator::GetFirst()
-{
-    assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
-    nCol = maParam.nCol1;
-    InitPos();
-    return GetThis();
-}
-
-bool ScQueryCellIterator::GetNext()
-{
-    IncPos();
-    if ( nStopOnMismatch )
-        nStopOnMismatch = nStopOnMismatchEnabled;
-    if ( nTestEqualCondition )
-        nTestEqualCondition = nTestEqualConditionEnabled;
-    return GetThis();
-}
-
-void ScQueryCellIterator::AdvanceQueryParamEntryField()
+template< ScQueryCellIteratorType iteratorType >
+void ScQueryCellIteratorBase< iteratorType >::AdvanceQueryParamEntryField()
 {
     SCSIZE nEntries = maParam.GetEntryCount();
     for ( SCSIZE j = 0; j < nEntries; j++  )
@@ -463,147 +476,6 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( 
SCCOL& nFoundCol,
     return (nFoundCol <= rDoc.MaxCol()) && (nFoundRow <= rDoc.MaxRow());
 }
 
-ScCountIfCellIterator::ScCountIfCellIterator(ScDocument& rDocument, const 
ScInterpreterContext& rContext, SCTAB nTable,
-             const ScQueryParam& rParam ) :
-    maParam(rParam),
-    rDoc( rDocument ),
-    mrContext( rContext ),
-    nTab( nTable)
-{
-    maParam.nCol1 = 
rDoc.maTabs[nTable]->ClampToAllocatedColumns(maParam.nCol1);
-    maParam.nCol2 = 
rDoc.maTabs[nTable]->ClampToAllocatedColumns(maParam.nCol2);
-    nCol = maParam.nCol1;
-    nRow = maParam.nRow1;
-}
-
-void ScCountIfCellIterator::InitPos()
-{
-    nRow = maParam.nRow1;
-    if (maParam.bHasHeader && maParam.bByRow)
-        ++nRow;
-    ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
-    maCurPos = pCol->maCells.position(nRow);
-}
-
-void ScCountIfCellIterator::IncPos()
-{
-    if (maCurPos.second + 1 < maCurPos.first->size)
-    {
-        // Move within the same block.
-        ++maCurPos.second;
-        ++nRow;
-    }
-    else
-        // Move to the next block.
-        IncBlock();
-}
-
-void ScCountIfCellIterator::IncBlock()
-{
-    ++maCurPos.first;
-    maCurPos.second = 0;
-
-    nRow = maCurPos.first->position;
-}
-
-int ScCountIfCellIterator::GetCount()
-{
-    assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, 
FIX IT");
-    nCol = maParam.nCol1;
-    InitPos();
-
-    const ScQueryEntry& rEntry = maParam.GetEntry(0);
-    const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
-    const bool bSingleQueryItem = rEntry.GetQueryItems().size() == 1;
-    int count = 0;
-    ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], maParam, 
&mrContext);
-
-    ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
-    while (true)
-    {
-        bool bNextColumn = maCurPos.first == pCol->maCells.end();
-        if (!bNextColumn)
-        {
-            if (nRow > maParam.nRow2)
-                bNextColumn = true;
-        }
-
-        if (bNextColumn)
-        {
-            do
-            {
-                ++nCol;
-                if (nCol > maParam.nCol2 || nCol >= 
rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
-                    return count;
-                AdvanceQueryParamEntryField();
-                pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
-            }
-            while (!rItem.mbMatchEmpty && pCol->IsEmptyData());
-
-            InitPos();
-        }
-
-        if (maCurPos.first->type == sc::element_type_empty)
-        {
-            if (rItem.mbMatchEmpty && bSingleQueryItem)
-            {
-                // This shortcut, instead of determining if any SC_OR query
-                // exists or this query is SC_AND'ed (which wouldn't make
-                // sense, but..) and evaluating them in ValidQuery(), is
-                // possible only because the interpreter is the only caller
-                // that sets mbMatchEmpty and there is only one item in those
-                // cases.
-                // XXX this would have to be reworked if other filters used it
-                // in different manners and evaluation would have to be done in
-                // ValidQuery().
-                count++;
-                IncPos();
-                continue;
-            }
-            else
-            {
-                IncBlock();
-                continue;
-            }
-        }
-
-        ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
-
-        if ( queryEvaluator.ValidQuery( nRow,
-                (nCol == static_cast<SCCOL>(rEntry.nField) ? &aCell : 
nullptr)))
-        {
-            if (aCell.isEmpty())
-                return count;
-            count++;
-            IncPos();
-            continue;
-        }
-        else
-            IncPos();
-    }
-    return count;
-}
-
-void ScCountIfCellIterator::AdvanceQueryParamEntryField()
-{
-    SCSIZE nEntries = maParam.GetEntryCount();
-    for ( SCSIZE j = 0; j < nEntries; j++  )
-    {
-        ScQueryEntry& rEntry = maParam.GetEntry( j );
-        if ( rEntry.bDoQuery )
-        {
-            if ( rEntry.nField < rDoc.MaxCol() )
-                rEntry.nField++;
-            else
-            {
-                OSL_FAIL( "AdvanceQueryParamEntryField: ++rEntry.nField > 
MAXCOL" );
-            }
-        }
-        else
-            break;  // for
-    }
-}
-
 namespace {
 
 template<typename Iter>
@@ -1087,4 +959,62 @@ bool ScQueryCellIterator::BinarySearch()
     }
 }
 
+
+template<>
+bool ScQueryCellIteratorBase< ScQueryCellIteratorType::Generic 
>::HandleItemFound()
+{
+    getThisResult = true;
+    return true; // Return from PerformQuery().
+}
+
+bool ScQueryCellIterator::GetThis()
+{
+    getThisResult = false;
+    PerformQuery();
+    return getThisResult;
+}
+
+bool ScQueryCellIterator::GetFirst()
+{
+    assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+    nCol = maParam.nCol1;
+    InitPos();
+    return GetThis();
+}
+
+bool ScQueryCellIterator::GetNext()
+{
+    IncPos();
+    if ( nStopOnMismatch )
+        nStopOnMismatch = nStopOnMismatchEnabled;
+    if ( nTestEqualCondition )
+        nTestEqualCondition = nTestEqualConditionEnabled;
+    return GetThis();
+}
+
+
+template<>
+bool ScQueryCellIteratorBase< ScQueryCellIteratorType::CountIf 
>::HandleItemFound()
+{
+    ++countIfCount;
+    return false; // Continue searching.
+}
+
+sal_uInt64 ScCountIfCellIterator::GetCount()
+{
+    // Keep Entry.nField in iterator on column change
+    SetAdvanceQueryParamEntryField( true );
+    assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, 
FIX IT");
+    maParam.nCol1 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol1);
+    maParam.nCol2 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol2);
+    nCol = maParam.nCol1;
+    InitPos();
+    countIfCount = 0;
+    PerformQuery();
+    return countIfCount;
+}
+
+template class ScQueryCellIteratorBase< ScQueryCellIteratorType::Generic >;
+template class ScQueryCellIteratorBase< ScQueryCellIteratorType::CountIf >;
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index aacfa42ec8ea..c3f095cfa526 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5786,7 +5786,7 @@ void ScInterpreter::ScCountIf()
             }
             else
             {
-                ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, 
rParam);
+                ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
                 fCount += aCellIter.GetCount();
             }
         }

Reply via email to