This is an automated email from the ASF dual-hosted git repository.

jimjag pushed a commit to branch openformula
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 8b480056113962bde68c71eaf956814a63b9ed7c
Author: Jim Jagielski <[email protected]>
AuthorDate: Fri Nov 13 10:17:12 2026 -0500

    sc: do not count rows without content as records in DCOUNT and DCOUNTA
    
    Without a field, blank rows in the database range matched "=" and "<>x".
---
 main/sc/source/core/data/dociter.cxx  | 27 ++++++++++++++++++++++++---
 test/odf-validate/calcval-cases14.tsv |  5 +++++
 test/odf-validate/calcval.py          |  2 ++
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/main/sc/source/core/data/dociter.cxx 
b/main/sc/source/core/data/dociter.cxx
index 98d866e94f..7f23ae0ecf 100644
--- a/main/sc/source/core/data/dociter.cxx
+++ b/main/sc/source/core/data/dociter.cxx
@@ -688,10 +688,28 @@ SCSIZE 
ScDBQueryDataIterator::DataAccessInternal::countRecords()
     // getCurrent() only visits rows that have a cell in column nCol.
     ScQueryParam aParam(*mpParam);
     SCROW nFirstRow = mpParam->bHasHeader ? mpParam->nRow1 + 1 : 
mpParam->nRow1;
+    SCROW nLastRow = mpParam->nRow2;
+    if (nFirstRow > nLastRow)
+        return 0;
+    // A row without any content in the database columns is not a record.
+    ::std::vector<bool> aHasContent(nLastRow - nFirstRow + 1, false);
+    for (SCCOL nC = mpParam->nCol1; nC <= mpParam->nCol2; ++nC)
+    {
+        SCSIZE nCellCount = mpDoc->GetCellCount(nTab, nC);
+        for (SCSIZE i = ScDBQueryDataIterator::SearchColEntryIndex(*mpDoc, 
nTab, nFirstRow, nC); i < nCellCount; ++i)
+        {
+            SCROW nThisRow = 
ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nC, i);
+            if (nThisRow > nLastRow)
+                break;
+            ScBaseCell* pCell = 
ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nC, i);
+            if (nThisRow >= nFirstRow && pCell && pCell->GetCellType() != 
CELLTYPE_NOTE)
+                aHasContent[nThisRow - nFirstRow] = true;
+        }
+    }
     SCSIZE nCount = 0;
-    for (SCROW nR = nFirstRow; nR <= mpParam->nRow2; ++nR)
+    for (SCROW nR = nFirstRow; nR <= nLastRow; ++nR)
     {
-        if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, aParam, nTab, nR, 
NULL))
+        if (aHasContent[nR - nFirstRow] && 
ScDBQueryDataIterator::IsQueryValid(*mpDoc, aParam, nTab, nR, NULL))
             ++nCount;
     }
     return nCount;
@@ -758,7 +776,10 @@ SCSIZE 
ScDBQueryDataIterator::DataAccessMatrix::countRecords()
     SCSIZE nCount = 0;
     for (SCROW nR = mpParam->bHasHeader ? 1 : 0; nR < mnRows; ++nR)
     {
-        if (isValidQuery(nR, rMat))
+        bool bHasContent = false;
+        for (SCCOL nC = 0; nC < mnCols && !bHasContent; ++nC)
+            bHasContent = !rMat.IsEmpty(nC, nR);
+        if (bHasContent && isValidQuery(nR, rMat))
             ++nCount;
     }
     return nCount;
diff --git a/test/odf-validate/calcval-cases14.tsv 
b/test/odf-validate/calcval-cases14.tsv
index 9266fb4e6d..918bfea06e 100644
--- a/test/odf-validate/calcval-cases14.tsv
+++ b/test/odf-validate/calcval-cases14.tsv
@@ -181,6 +181,11 @@ DB.DCOUNT.nf.inline        
=DCOUNT({"Name";"Age";"Score"|"A";20;5|"B";30;"n/a"|"C";40;1
 DB.DCOUNTA.nf.inline   
=DCOUNTA({"Name";"Age";"Score"|"A";20;5|"B";30;"n/a"|"C";40;15|"D";50;"x"|"E";60;25};;Y1:Y2)
    cell    1       6.9.4 Field omitted, inline array as the database: Name A 
matches one record
 DB.DCOUNT.nf.nocrit    =DCOUNT(AX11:AY14;;AZ11:AZ12)   cell    -       Field 
omitted, criteria cell empty (AZ12). 4.11.11 reads an empty criterion as 0 (no 
match); AOO ignores empty criteria (known deviation, see DCOUNT.emptycrit). 
Record only
 DB.DCOUNT.f.nocrit     =DCOUNT(AX11:AY14;"V";AZ11:AZ12)        cell    -       
Same as DB.DCOUNT.nf.nocrit with field V. Record only
+DB.DCOUNT.nf.ne.blank  =DCOUNT(R1:T10;;AP1:AP2)        cell    4       6.9.3 
Field omitted; R1:T10 has four blank rows below the data; a row with no content 
is not a record: B,C,D,E
+DB.DCOUNT.nf.eq.blank  =DCOUNT(R1:T10;;AQ1:AQ2)        cell    1       6.9.3 
Field omitted; Score "=" matches D, not the blank rows
+DB.DCOUNTA.nf.ne.blank =DCOUNTA(R1:T10;;AP1:AP2)       cell    4       6.9.4 
Field omitted; same record count with blank rows
+DB.DCOUNT.nf.ne.col    =DCOUNT(R1:T1048576;;AP1:AP2)   cell    4       6.9.3 
Field omitted; whole-column database range, the blank rows below the data are 
not records
+DB.DCOUNT.nf.eq.gap    =DCOUNT(BB1:BC5;;BD1:BD2)       cell    1       6.9.3 
Field omitted; K "="; row 3 has K empty and V 2 (a record), row 4 is blank (not 
a record)
 BITAND.3params =BITAND(6;3;1)  cell    #ERR    6.6.2 BITAND exactly 2 params 
enforced
 
 # OpenFormula Small Group: basic limits, syntax, operators and conversions 
(ODF 1.4 Part 4 sections 3.7, 5, 6.3, 6.4).
diff --git a/test/odf-validate/calcval.py b/test/odf-validate/calcval.py
index 64e7439416..dc322a1e2e 100644
--- a/test/odf-validate/calcval.py
+++ b/test/odf-validate/calcval.py
@@ -150,6 +150,8 @@ def setup_data(doc):
     put("AV1", [["Score", "Age"], ["25", None], [None, "50"]])
     put("AX11", [["K", "V"], ["a", 1], ["b", 2], ["c", 3]])
     put("AZ11", [["K"], [None]])
+    put("BB1", [["K", "V"], ["a", 1], [None, 2], [None, None], ["c", 3]])
+    put("BD1", [["K"], ["="]])
     return sh
 
 

Reply via email to