LibreOffice Module sc (master) 1
queryiter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <queryiter.hxx>
21
23#include <o3tl/safeint.hxx>
24#include <svl/numformat.hxx>
25#include <svl/zforlist.hxx>
26
27#include <global.hxx>
28#include <dociter.hxx>
29#include <document.hxx>
30#include <table.hxx>
31#include <column.hxx>
32#include <formulacell.hxx>
33#include <attarray.hxx>
34#include <patattr.hxx>
35#include <docoptio.hxx>
36#include <cellform.hxx>
37#include <segmenttree.hxx>
38#include <progress.hxx>
39#include <queryparam.hxx>
40#include <queryentry.hxx>
41#include <globstr.hrc>
42#include <scresid.hxx>
43#include <cellvalue.hxx>
44#include <scmatrix.hxx>
45#include <rowheightcontext.hxx>
46#include <queryevaluator.hxx>
47#include <rangecache.hxx>
48#include <refdata.hxx>
49
50#include <o3tl/safeint.hxx>
51#include <tools/fract.hxx>
52#include <editeng/editobj.hxx>
53#include <svl/sharedstring.hxx>
55#include <osl/diagnose.h>
56#include <sal/log.hxx>
57
58#include <algorithm>
59#include <limits>
60#include <vector>
61
62template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
64 ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& rParam, bool bMod )
65 : AccessBase( rDocument, rContext, rParam )
66 , nStopOnMismatch( nStopOnMismatchDisabled )
67 , nTestEqualCondition( nTestEqualConditionDisabled )
68 , bAdvanceQuery( false )
69 , bIgnoreMismatchOnLeadingStrings( false )
70{
71 nTab = nTable;
72 nCol = maParam.nCol1;
73 nRow = maParam.nRow1;
74 SCSIZE i;
75 if (!bMod) // Or else it's already inserted
76 return;
77
78 SCSIZE nCount = maParam.GetEntryCount();
79 for (i = 0; (i < nCount) && (maParam.GetEntry(i).bDoQuery); ++i)
80 {
81 ScQueryEntry& rEntry = maParam.GetEntry(i);
82 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
83 sal_uInt32 nIndex = 0;
84 bool bNumber = mrContext.GetFormatTable()->IsNumberFormat(
85 rItem.maString.getString(), nIndex, rItem.mfVal);
87 }
88}
89
90template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
92{
93 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
94 const ScQueryEntry& rEntry = maParam.GetEntry(0);
95 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
96
97 const bool bSingleQueryItem = rEntry.GetQueryItems().size() == 1;
98 SCCOLROW nFirstQueryField = rEntry.nField;
99 bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings &&
101 bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
102 !maParam.bHasHeader && rItem.meType == ScQueryEntry::ByString &&
103 ((maParam.bByRow && nRow == maParam.nRow1) ||
104 (!maParam.bByRow && nCol == maParam.nCol1));
105 bool bTestEqualCondition = false;
106 ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], maParam, &mrContext,
107 (nTestEqualCondition ? &bTestEqualCondition : nullptr));
108 if( queryType == ScQueryCellIteratorType::CountIf )
109 {
110 // These are not used for COUNTIF, so should not be set, make the compiler
111 // explicitly aware of it so that the relevant parts are optimized away.
112 assert( !bAllStringIgnore );
113 assert( !bIgnoreMismatchOnLeadingStrings );
114 assert( nStopOnMismatch == nStopOnMismatchDisabled );
115 assert( nTestEqualCondition == nTestEqualConditionDisabled );
116 bAllStringIgnore = false;
117 bIgnoreMismatchOnLeadingStrings = false;
118 nStopOnMismatch = nStopOnMismatchDisabled;
119 nTestEqualCondition = nTestEqualConditionDisabled;
120 // This one is always set.
121 assert( bAdvanceQuery );
122 bAdvanceQuery = true;
123 }
124
125 const ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
126 while (true)
127 {
128 bool bNextColumn = maCurPos.first == pCol->maCells.end();
129 if (!bNextColumn)
130 {
131 if (nRow > maParam.nRow2)
132 bNextColumn = true;
133 }
134
135 if (bNextColumn)
136 {
137 do
138 {
139 ++nCol;
140 if (nCol > maParam.nCol2 || nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
141 return;
142 if ( bAdvanceQuery )
143 {
144 AdvanceQueryParamEntryField();
145 nFirstQueryField = rEntry.nField;
146 }
147 pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
148 }
149 while (!rItem.mbMatchEmpty && pCol->IsEmptyData());
150
151 InitPos();
152
153 bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
154 !maParam.bHasHeader && rItem.meType == ScQueryEntry::ByString &&
155 maParam.bByRow;
156 }
157
158 if (maCurPos.first->type == sc::element_type_empty)
159 {
160 if (rItem.mbMatchEmpty && bSingleQueryItem)
161 {
162 // This shortcut, instead of determining if any SC_OR query
163 // exists or this query is SC_AND'ed (which wouldn't make
164 // sense, but..) and evaluating them in ValidQuery(), is
165 // possible only because the interpreter is the only caller
166 // that sets mbMatchEmpty and there is only one item in those
167 // cases.
168 // XXX this would have to be reworked if other filters used it
169 // in different manners and evaluation would have to be done in
170 // ValidQuery().
171 if(HandleItemFound())
172 return;
173 IncPos();
174 continue;
175 }
176 else
177 {
178 IncBlock();
179 continue;
180 }
182
183 ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
184
185 if (bAllStringIgnore && aCell.hasString())
186 IncPos();
187 else
188 {
189 if ( queryEvaluator.ValidQuery( nRow,
190 (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : nullptr)))
191 {
192 if ( nTestEqualCondition && bTestEqualCondition )
193 nTestEqualCondition |= nTestEqualConditionMatched;
194 if ( aCell.isEmpty())
195 return;
196 if( HandleItemFound())
197 return;
198 IncPos();
199 continue;
200 }
201 else if ( nStopOnMismatch )
202 {
203 // Yes, even a mismatch may have a fulfilled equal
204 // condition if regular expressions were involved and
205 // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried.
206 if ( nTestEqualCondition && bTestEqualCondition )
207 {
208 nTestEqualCondition |= nTestEqualConditionMatched;
209 nStopOnMismatch |= nStopOnMismatchOccurred;
210 return;
211 }
212 bool bStop;
213 if (bFirstStringIgnore)
214 {
215 if (aCell.hasString())
216 {
217 IncPos();
218 bStop = false;
219 }
220 else
221 bStop = true;
222 }
223 else
224 bStop = true;
225 if (bStop)
226 {
227 nStopOnMismatch |= nStopOnMismatchOccurred;
228 return;
229 }
230 }
231 else
232 IncPos();
233 }
234 bFirstStringIgnore = false;
235 }
236}
237
238template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
241 if constexpr( accessType != ScQueryCellIteratorAccess::SortedCache )
242 AccessBase::InitPos();
243 else
244 {
245 // This should be all in AccessBase::InitPos(), but that one can't call
246 // BinarySearch(), so do it this way instead.
247 AccessBase::InitPosStart();
248 ScQueryOp& op = maParam.GetEntry(0).eOp;
249 SCROW beforeRow = -1;
250 SCROW lastRow = -1;
251 if( op == SC_EQUAL )
252 {
253 if( BinarySearch( nCol ))
254 {
255 // BinarySearch() searches for the last item that matches. Now we
256 // also need to find the first item where to start. Find the last
257 // non-matching position using SC_LESS and the start position
258 // is the one after it.
259 lastRow = nRow;
260 ScQueryOp saveOp = op;
261 op = SC_LESS;
262 if( BinarySearch( nCol, true ))
263 beforeRow = nRow;
264 // If BinarySearch() returns false, there was no match, which means
265 // there's no value smaller. In that case BinarySearch() has set
266 // the position to the first row in the range.
267 op = saveOp; // back to SC_EQUAL
268 }
269 else if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
270 && rDoc.IsEmptyData(nCol, maParam.nRow1, nCol, maParam.nRow2, nTab))
271 {
272 // BinarySearch() returns false in case it's all empty data,
273 // handle that specially.
274 beforeRow = -1;
275 lastRow = maParam.nRow2;
276 }
277 }
278 else
279 { // The range is from the start up to and including the last matching.
280 if( BinarySearch( nCol ))
281 lastRow = nRow;
282 }
283 AccessBase::InitPosFinish( beforeRow, lastRow );
284 }
285}
286
287template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
290 SCSIZE nEntries = maParam.GetEntryCount();
291 for ( SCSIZE j = 0; j < nEntries; j++ )
292 {
293 ScQueryEntry& rEntry = maParam.GetEntry( j );
294 if ( rEntry.bDoQuery )
295 {
296 if ( rEntry.nField < rDoc.MaxCol() )
297 rEntry.nField++;
298 else
299 {
300 assert(!"AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL");
301 }
302 }
303 else
304 break; // for
305 }
306}
307
308namespace {
309
310template<typename Iter>
311void incBlock(std::pair<Iter, size_t>& rPos)
313 // Move to the next block.
314 ++rPos.first;
315 rPos.second = 0;
316}
317
318template<typename Iter>
319void decBlock(std::pair<Iter, size_t>& rPos)
320{
321 // Move to the last element of the previous block.
322 --rPos.first;
323 rPos.second = rPos.first->size - 1;
324}
325
326}
327
328template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
330{
331 assert(maParam.GetEntry(0).bDoQuery && !maParam.GetEntry(1).bDoQuery
332 && maParam.GetEntry(0).GetQueryItems().size() == 1 );
333 assert(maParam.eSearchType == utl::SearchParam::SearchType::Normal);
334 assert(maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString
335 || maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue);
336 assert(maParam.bByRow);
337 assert(maParam.GetEntry(0).eOp == SC_LESS || maParam.GetEntry(0).eOp == SC_LESS_EQUAL
338 || maParam.GetEntry(0).eOp == SC_GREATER || maParam.GetEntry(0).eOp == SC_GREATER_EQUAL
339 || maParam.GetEntry(0).eOp == SC_EQUAL);
340
341 // TODO: This will be extremely slow with mdds::multi_type_vector.
342
343 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
344 nCol = col;
345 nRow = maParam.nRow1;
346
347 if (nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
348 return false;
349
350 const ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
351 if (pCol->IsEmptyData())
352 return false;
353
354 CollatorWrapper& rCollator = ScGlobal::GetCollator(maParam.bCaseSens);
355 SvNumberFormatter& rFormatter = *(mrContext.GetFormatTable());
356 const ScQueryEntry& rEntry = maParam.GetEntry(0);
357 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
358 bool bAscending = rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_EQUAL;
359 bool bByString = rItem.meType == ScQueryEntry::ByString;
360 bool bForceStr = bByString && ( rEntry.eOp == SC_EQUAL || forEqual );
361 bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString;
362 bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
363 !maParam.bHasHeader && bByString;
364
365 if (maParam.bHasHeader)
366 ++nRow;
367
368 if (bFirstStringIgnore)
369 {
370 sc::CellStoreType::const_position_type aPos = pCol->maCells.position(nRow);
371 if (aPos.first->type == sc::element_type_string || aPos.first->type == sc::element_type_edittext)
372 {
373 ScRefCellValue aCell = sc::toRefCell(aPos.first, aPos.second);
374 sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, nRow);
375 OUString aCellStr = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
376 sal_Int32 nTmp = rCollator.compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
377 if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) ||
378 (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) ||
379 (rEntry.eOp == SC_EQUAL && nTmp != 0) ||
380 (rEntry.eOp == SC_LESS && nTmp >= 0) ||
381 (rEntry.eOp == SC_GREATER && nTmp <= 0))
382 ++nRow;
383 }
384 }
385
386 // Skip leading empty block, if any.
387 sc::CellStoreType::const_position_type startPos = pCol->maCells.position(nRow);
388 if (startPos.first->type == sc::element_type_empty)
389 incBlock(startPos);
390 if(bAllStringIgnore)
391 {
392 // Skip all leading string or empty blocks.
393 while (startPos.first != pCol->maCells.end()
394 && (startPos.first->type == sc::element_type_string ||
395 startPos.first->type == sc::element_type_edittext ||
396 startPos.first->type == sc::element_type_empty))
397 {
398 incBlock(startPos);
399 }
400 }
401 if(startPos.first == pCol->maCells.end())
402 return false;
403 nRow = startPos.first->position + startPos.second;
404 if (nRow > maParam.nRow2)
405 return false;
406
407 auto aIndexer = MakeBinarySearchIndexer(pCol->maCells, nRow, maParam.nRow2);
408 if (!aIndexer.isValid())
409 return false;
410
411 size_t nLo = aIndexer.getLowIndex();
412 size_t nHi = aIndexer.getHighIndex();
413 BinarySearchCellType aCellData;
414
415 // Bookkeeping values for breaking up the binary search in case the data
416 // range isn't strictly sorted.
417 size_t nLastInRange = nLo;
418 double fLastInRangeValue = bAscending ?
419 -(::std::numeric_limits<double>::max()) :
420 ::std::numeric_limits<double>::max();
421 OUString aLastInRangeString;
422 if (!bAscending)
423 aLastInRangeString = OUString(u'\xFFFF');
424
425 aCellData = aIndexer.getCell(nLastInRange);
426 ScRefCellValue aCell = aCellData.first;
427 if (bForceStr || aCell.hasString())
428 {
429 sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
430 OUString aStr = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
431 aLastInRangeString = aStr;
432 }
433 else
434 {
435 switch (aCell.getType())
436 {
437 case CELLTYPE_VALUE :
438 fLastInRangeValue = aCell.getDouble();
439 break;
440 case CELLTYPE_FORMULA :
441 fLastInRangeValue = aCell.getFormula()->GetValue();
442 break;
443 default:
444 {
445 // added to avoid warnings
446 }
447 }
448 }
449
450 sal_Int32 nRes = 0;
451 std::optional<size_t> found;
452 bool bDone = false;
453 bool orderBroken = false;
454 while (nLo <= nHi && !bDone)
455 {
456 size_t nMid = (nLo+nHi)/2;
457 size_t i = nMid;
458
459 aCellData = aIndexer.getCell(i);
460 aCell = aCellData.first;
461 bool bStr = bForceStr || aCell.hasString();
462 nRes = 0;
463
464 // compares are content<query:-1, content>query:1
465 // Cell value comparison similar to ScTable::ValidQuery()
466 if (!bStr && !bByString)
467 {
468 double nCellVal;
469 switch (aCell.getType())
470 {
471 case CELLTYPE_VALUE :
472 case CELLTYPE_FORMULA :
473 nCellVal = aCell.getValue();
474 break;
475 default:
476 nCellVal = 0.0;
477 }
478 if ((nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(
479 nCellVal, rItem.mfVal))
480 {
481 nRes = -1;
482 if (bAscending)
483 {
484 if (fLastInRangeValue <= nCellVal)
485 {
486 fLastInRangeValue = nCellVal;
487 nLastInRange = i;
488 }
489 else if (fLastInRangeValue >= nCellVal)
490 {
491 // not strictly sorted, continue with GetThis()
492 orderBroken = true;
493 bDone = true;
494 }
495 }
496 }
497 else if ((nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(
498 nCellVal, rItem.mfVal))
499 {
500 nRes = 1;
501 if (!bAscending)
502 {
503 if (fLastInRangeValue >= nCellVal)
504 {
505 fLastInRangeValue = nCellVal;
506 nLastInRange = i;
507 }
508 else if (fLastInRangeValue <= nCellVal)
509 {
510 // not strictly sorted, continue with GetThis()
511 orderBroken = true;
512 bDone = true;
513 }
514 }
515 }
516 }
517 else if (bStr && bByString)
518 {
519 sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
520 OUString aCellStr = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
521
522 nRes = rCollator.compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
523 if (nRes < 0 && bAscending)
524 {
525 sal_Int32 nTmp = rCollator.compareString( aLastInRangeString,
526 aCellStr);
527 if (nTmp <= 0)
528 {
529 aLastInRangeString = aCellStr;
530 nLastInRange = i;
531 }
532 else if (nTmp > 0)
533 {
534 // not strictly sorted, continue with GetThis()
535 orderBroken = true;
536 bDone = true;
537 }
538 }
539 else if (nRes > 0 && !bAscending)
540 {
541 sal_Int32 nTmp = rCollator.compareString( aLastInRangeString,
542 aCellStr);
543 if (nTmp >= 0)
544 {
545 aLastInRangeString = aCellStr;
546 nLastInRange = i;
547 }
548 else if (nTmp < 0)
549 {
550 // not strictly sorted, continue with GetThis()
551 orderBroken = true;
552 bDone = true;
553 }
554 }
555 }
556 else if (!bStr && bByString)
557 {
558 nRes = -1; // numeric < string
559 if (bAscending)
560 nLastInRange = i;
561 }
562 else // if (bStr && !bByString)
563 {
564 nRes = 1; // string > numeric
565 if (!bAscending)
566 nLastInRange = i;
567 }
568 if (nRes < 0)
569 {
570 if (bAscending)
571 nLo = nMid + 1;
572 else // assumed to be SC_GREATER_EQUAL
573 {
574 if (nMid > 0)
575 nHi = nMid - 1;
576 else
577 bDone = true;
578 }
579 }
580 else if (nRes > 0)
581 {
582 if (bAscending)
583 {
584 if (nMid > 0)
585 nHi = nMid - 1;
586 else
587 bDone = true;
588 }
589 else // assumed to be SC_GREATER_EQUAL
590 nLo = nMid + 1;
591 }
592 else
593 {
594 if(rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL || rEntry.eOp == SC_EQUAL)
595 {
596 found = i;
597 nLastInRange = i;
598 // But keep searching to find the last matching one.
599 nLo = nMid + 1;
600 }
601 else if (bAscending)
602 {
603 if (nMid > 0)
604 nHi = nMid - 1;
605 else
606 bDone = true;
607 }
608 else
609 {
610 if (nMid > 0)
611 nHi = nMid - 1;
612 else
613 bDone = true;
614 }
615 }
616 }
617
618 bool isInRange;
619 if (orderBroken)
620 {
621 // Reset position to the first row in range and force caller
622 // to search from start.
623 nLo = aIndexer.getLowIndex();
624 isInRange = false;
625 }
626 else if (found)
627 {
628 nLo = *found;
629 isInRange = true;
630 }
631 else
632 {
633 // Not nothing was found and the search position is at the start,
634 // then the possible match would need to be before the data range.
635 // In that case return false to force the caller to search from the start
636 // and detect this.
637 isInRange = nLo != aIndexer.getLowIndex();
638 // If nothing was found, that is either because there is no value
639 // that would match exactly, or the data range is not properly sorted
640 // and we failed to detect (doing so reliably would require a linear scan).
641 // Set the position to the last one that was in matching range (i.e. before
642 // where the exact match would be), and leave sorting it out to GetThis()
643 // or whatever the caller uses.
644 nLo = nLastInRange;
645 }
646
647 aCellData = aIndexer.getCell(nLo);
648 if (nLo <= nHi && aCellData.second <= maParam.nRow2)
649 {
650 nRow = aCellData.second;
651 maCurPos = aIndexer.getPosition(nLo);
652 return isInRange;
653 }
654 else
655 {
656 nRow = maParam.nRow2 + 1;
657 // Set current position to the last possible row.
658 maCurPos.first = pCol->maCells.end();
659 --maCurPos.first;
660 maCurPos.second = maCurPos.first->size - 1;
661 return false;
662 }
663}
664
665
666template< ScQueryCellIteratorAccess accessType >
668 SCROW& nFoundRow )
669{
670 // Set and automatically reset mpParam->mbRangeLookup when returning.
671 comphelper::FlagRestorationGuard aRangeLookupResetter( maParam.mbRangeLookup, true );
672
673 nFoundCol = rDoc.MaxCol()+1;
674 nFoundRow = rDoc.MaxRow()+1;
675 SetStopOnMismatch( true ); // assume sorted keys
676 SetTestEqualCondition( true );
677 bIgnoreMismatchOnLeadingStrings = true;
678 bool bLiteral = maParam.eSearchType == utl::SearchParam::SearchType::Normal &&
679 maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString;
680 bool bBinary = maParam.bByRow &&
681 (bLiteral || maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue) &&
682 (maParam.GetEntry(0).eOp == SC_LESS_EQUAL || maParam.GetEntry(0).eOp == SC_GREATER_EQUAL);
683 bool bFound = false;
684 if (bBinary)
685 {
686 if (BinarySearch( maParam.nCol1 ))
687 {
688 // BinarySearch() already positions correctly and only needs real
689 // query comparisons afterwards, skip the verification check below.
690 maParam.mbRangeLookup = false;
691 bFound = GetThis();
692 }
693 else // Not sorted properly, or before the range (in which case GetFirst() will be simple).
694 bFound = GetFirst();
695 }
696 else
697 {
698 bFound = GetFirst();
699 }
700 if (bFound)
701 {
702 // First equal entry or last smaller than (greater than) entry.
703 PositionType aPosSave;
704 bool bNext = false;
705 SCSIZE nEntries = maParam.GetEntryCount();
706 std::vector<SCCOL> aFoundFieldPositions(nEntries);
707 do
708 {
709 nFoundCol = GetCol();
710 nFoundRow = GetRow();
711 aPosSave = maCurPos;
712 // If we might need to rewind below, save the position to rewind to
713 // rather than calculate it as a diff between nCol and nFoundCol as
714 // PerformQuery can return early if nCol is greater than
715 // maParam.nCol2 or AllocatedColumns
716 if (maParam.mbRangeLookup && bAdvanceQuery)
717 {
718 for (SCSIZE j=0; j < nEntries; ++j)
719 {
720 ScQueryEntry& rEntry = maParam.GetEntry( j );
721 if (rEntry.bDoQuery)
722 aFoundFieldPositions[j] = maParam.GetEntry(j).nField;
723 else
724 break; // for
725 }
726 }
727 if (IsEqualConditionFulfilled())
728 break;
729 bNext = GetNext();
730 }
731 while (bNext);
732
733 // There may be no pNext but equal condition fulfilled if regular
734 // expressions are involved. Keep the found entry and proceed.
735 if (!bNext && !IsEqualConditionFulfilled())
736 {
737 // Step back to last in range and adjust position markers for
738 // GetNumberFormat() or similar.
739 bool bColDiff = nCol != nFoundCol;
740 nCol = nFoundCol;
741 nRow = nFoundRow;
742 maCurPos = aPosSave;
743 if (maParam.mbRangeLookup)
744 {
745 // Verify that the found entry does not only fulfill the range
746 // lookup but also the real query, i.e. not numeric was found
747 // if query is ByString and vice versa.
748 maParam.mbRangeLookup = false;
749 // Step back the last field advance if GetNext() did one.
750 if (bAdvanceQuery && bColDiff)
751 {
752 for (SCSIZE j=0; j < nEntries; ++j)
753 {
754 ScQueryEntry& rEntry = maParam.GetEntry( j );
755 if (rEntry.bDoQuery)
756 {
757 rEntry.nField = aFoundFieldPositions[j];
758 assert(rEntry.nField >= 0);
759 }
760 else
761 break; // for
762 }
763 }
764 // Check it.
765 if (!GetThis())
766 {
767 nFoundCol = rDoc.MaxCol()+1;
768 nFoundRow = rDoc.MaxRow()+1;
769 }
770 }
771 }
772 }
773 if ( IsEqualConditionFulfilled() )
774 {
775 // Position on last equal entry.
776 SCSIZE nEntries = maParam.GetEntryCount();
777 for ( SCSIZE j = 0; j < nEntries; j++ )
778 {
779 ScQueryEntry& rEntry = maParam.GetEntry( j );
780 if ( rEntry.bDoQuery )
781 {
782 switch ( rEntry.eOp )
783 {
784 case SC_LESS_EQUAL :
785 case SC_GREATER_EQUAL :
786 rEntry.eOp = SC_EQUAL;
787 break;
788 default:
789 {
790 // added to avoid warnings
791 }
792 }
793 }
794 else
795 break; // for
796 }
797 PositionType aPosSave;
798 bIgnoreMismatchOnLeadingStrings = false;
799 SetTestEqualCondition( false );
800 do
801 {
802 nFoundCol = GetCol();
803 nFoundRow = GetRow();
804 aPosSave = maCurPos;
805 } while (GetNext());
806
807 // Step back conditions are the same as above
808 nCol = nFoundCol;
809 nRow = nFoundRow;
810 maCurPos = aPosSave;
811 return true;
812 }
813 if ( (maParam.eSearchType != utl::SearchParam::SearchType::Normal) &&
814 StoppedOnMismatch() )
815 {
816 // Assume found entry to be the last value less than respectively
817 // greater than the query. But keep on searching for an equal match.
818 SCSIZE nEntries = maParam.GetEntryCount();
819 for ( SCSIZE j = 0; j < nEntries; j++ )
820 {
821 ScQueryEntry& rEntry = maParam.GetEntry( j );
822 if ( rEntry.bDoQuery )
823 {
824 switch ( rEntry.eOp )
825 {
826 case SC_LESS_EQUAL :
827 case SC_GREATER_EQUAL :
828 rEntry.eOp = SC_EQUAL;
829 break;
830 default:
831 {
832 // added to avoid warnings
833 }
834 }
835 }
836 else
837 break; // for
838 }
839 SetStopOnMismatch( false );
840 SetTestEqualCondition( false );
841 if (GetNext())
842 {
843 // Last of a consecutive area, avoid searching the entire parameter
844 // range as it is a real performance bottleneck in case of regular
845 // expressions.
846 PositionType aPosSave;
847 do
848 {
849 nFoundCol = GetCol();
850 nFoundRow = GetRow();
851 aPosSave = maCurPos;
852 SetStopOnMismatch( true );
853 } while (GetNext());
854 nCol = nFoundCol;
855 nRow = nFoundRow;
856 maCurPos = aPosSave;
857 }
858 }
859 return (nFoundCol <= rDoc.MaxCol()) && (nFoundRow <= rDoc.MaxRow());
860}
861
862// Direct linear cell access using mdds.
863
866 ScInterpreterContext& rContext, const ScQueryParam& rParam )
867 : maParam( rParam )
868 , rDoc( rDocument )
869 , mrContext( rContext )
870{
871 // coverity[uninit_member] - this just contains data, subclass will initialize some of it
872}
873
875{
876 nRow = maParam.nRow1;
877 if (maParam.bHasHeader && maParam.bByRow)
878 ++nRow;
879 const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
880 maCurPos = rCol.maCells.position(nRow);
881}
882
884{
885 if (maCurPos.second + 1 < maCurPos.first->size)
886 {
887 // Move within the same block.
888 ++maCurPos.second;
889 ++nRow;
890 }
891 else
892 // Move to the next block.
893 IncBlock();
894}
895
897{
898 ++maCurPos.first;
899 maCurPos.second = 0;
900
901 nRow = maCurPos.first->position;
902}
903
914{
915 typedef std::map<size_t, sc::CellStoreType::const_iterator> BlockMapType;
916
918
920
923
925
926public:
932 NonEmptyCellIndexer(
933 const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow) :
934 mrCells(rCells), mnLowIndex(0), mnHighIndex(0), mbValid(true)
935 {
936 // Find the low position.
937
938 sc::CellStoreType::const_position_type aLoPos = mrCells.position(nStartRow);
939 assert(aLoPos.first->type != sc::element_type_empty);
940 assert(aLoPos.first != rCells.end());
941
942 SCROW nFirstRow = aLoPos.first->position;
943 SCROW nLastRow = aLoPos.first->position + aLoPos.first->size - 1;
944
945 if (nFirstRow > nEndRow)
946 {
947 // Both start and end row positions are within the leading skipped
948 // blocks.
949 mbValid = false;
950 return;
951 }
952
953 // Calculate the index of the low position.
954 if (nFirstRow < nStartRow)
955 mnLowIndex = nStartRow - nFirstRow;
956 else
957 {
958 // Start row is within the skipped block(s). Set it to the first
959 // element of the low block.
960 mnLowIndex = 0;
961 }
962
963 if (nEndRow < nLastRow)
964 {
965 assert(nEndRow >= nFirstRow);
966 mnHighIndex = nEndRow - nFirstRow;
967
968 maBlockMap.emplace(aLoPos.first->size, aLoPos.first);
969 return;
970 }
971
972 // Find the high position.
973
974 sc::CellStoreType::const_position_type aHiPos = mrCells.position(aLoPos.first, nEndRow);
975 if (aHiPos.first->type == sc::element_type_empty)
976 {
977 // Move to the last position of the previous block.
978 decBlock(aHiPos);
979
980 // Check the row position of the end of the previous block, and make sure it's valid.
981 SCROW nBlockEndRow = aHiPos.first->position + aHiPos.first->size - 1;
982 if (nBlockEndRow < nStartRow)
983 {
984 mbValid = false;
985 return;
986 }
987 }
988
989 // Tag the start and end blocks, and all blocks in between in order
990 // but skip all empty blocks.
991
992 size_t nPos = 0;
993 sc::CellStoreType::const_iterator itBlk = aLoPos.first;
994 while (itBlk != aHiPos.first)
995 {
996 if (itBlk->type == sc::element_type_empty)
997 {
998 ++itBlk;
999 continue;
1000 }
1001
1002 nPos += itBlk->size;
1003 maBlockMap.emplace(nPos, itBlk);
1004 ++itBlk;
1005
1006 if (itBlk->type == sc::element_type_empty)
1007 ++itBlk;
1008
1009 assert(itBlk != mrCells.end());
1010 }
1011
1012 assert(itBlk == aHiPos.first);
1013 nPos += itBlk->size;
1014 maBlockMap.emplace(nPos, itBlk);
1015
1016 // Calculate the high index.
1017 BlockMapType::const_reverse_iterator ri = maBlockMap.rbegin();
1018 mnHighIndex = ri->first;
1019 mnHighIndex -= ri->second->size;
1020 mnHighIndex += aHiPos.second;
1021 }
1022
1023 sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
1024 {
1025 assert(mbValid);
1026 assert(mnLowIndex <= nIndex);
1027 assert(nIndex <= mnHighIndex);
1028
1029 sc::CellStoreType::const_position_type aRet(mrCells.end(), 0);
1030
1031 BlockMapType::const_iterator it = maBlockMap.upper_bound(nIndex);
1032 if (it == maBlockMap.end())
1033 return aRet;
1034
1035 sc::CellStoreType::const_iterator itBlk = it->second;
1036 size_t nBlkIndex = it->first - itBlk->size; // index of the first element of the block.
1037 assert(nBlkIndex <= nIndex);
1038 assert(nIndex < it->first);
1039
1040 size_t nOffset = nIndex - nBlkIndex;
1041 aRet.first = itBlk;
1042 aRet.second = nOffset;
1043 return aRet;
1044 }
1045
1046 BinarySearchCellType getCell( size_t nIndex ) const
1047 {
1048 BinarySearchCellType aRet;
1049 aRet.second = -1;
1050
1051 sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
1052 if (aPos.first == mrCells.end())
1053 return aRet;
1054
1055 aRet.first = sc::toRefCell(aPos.first, aPos.second);
1056 aRet.second = aPos.first->position + aPos.second;
1057 return aRet;
1058 }
1059
1060 size_t getLowIndex() const { return mnLowIndex; }
1061
1062 size_t getHighIndex() const { return mnHighIndex; }
1063
1064 bool isValid() const { return mbValid; }
1065};
1066
1069 const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow )
1070{
1071 return NonEmptyCellIndexer(rCells, nStartRow, nEndRow);
1072}
1073
1074// Sorted access using ScSortedRangeCache.
1075
1078 ScInterpreterContext& rContext, const ScQueryParam& rParam )
1079 : maParam( rParam )
1080 , rDoc( rDocument )
1081 , mrContext( rContext )
1082{
1083 // coverity[uninit_member] - this just contains data, subclass will initialize some of it
1084}
1085
1087 const ScSortedRangeCache& cache)
1088{
1089 sortedCache = &cache;
1090}
1091
1092// The idea in iterating using the sorted cache is that the iteration is instead done
1093// over indexes of the sorted cache (which is a stable sort of the cell contents) in the range
1094// that fits the query condition and then that is mapped to rows. This will result in iterating
1095// over only matching rows in their sorted order (and for equal rows in their row order).
1097{
1098 ScRange aSortedRangeRange( nCol, maParam.nRow1, nTab, nCol, maParam.nRow2, nTab );
1099 // We want all matching values first in the sort order,
1100 SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext ));
1101 // InitPosFinish() needs to be called after this, ScQueryCellIteratorBase::InitPos()
1102 // will handle that
1103}
1104
1106 SCROW beforeRow, SCROW lastRow )
1107{
1108 pColumn = &rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
1109 if(lastRow >= 0)
1110 {
1111 sortedCachePos = beforeRow >= 0 ? sortedCache->indexForRow(beforeRow) + 1 : 0;
1112 sortedCachePosLast = sortedCache->indexForRow(lastRow);
1113 if(sortedCachePos <= sortedCachePosLast)
1114 {
1115 nRow = sortedCache->rowForIndex(sortedCachePos);
1116 maCurPos = pColumn->maCells.position(nRow);
1117 return;
1118 }
1119 }
1120 // No rows, set to end.
1121 sortedCachePos = sortedCachePosLast = 0;
1122 maCurPos.first = pColumn->maCells.end();
1123 maCurPos.second = 0;
1124}
1125
1126template<bool fast>
1128{
1129 if(sortedCachePos < sortedCachePosLast)
1130 {
1131 ++sortedCachePos;
1132 nRow = sortedCache->rowForIndex(sortedCachePos);
1133#ifndef DBG_UTIL
1134 if constexpr (!fast)
1135#endif
1136 {
1137 // Avoid mdds position() call if row is in the same block.
1138 if(maCurPos.first != pColumn->maCells.end() && o3tl::make_unsigned(nRow) >= maCurPos.first->position
1139 && o3tl::make_unsigned(nRow) < maCurPos.first->position + maCurPos.first->size)
1140 maCurPos.second = nRow - maCurPos.first->position;
1141 else
1142 maCurPos = pColumn->maCells.position(nRow);
1143 }
1144 return true;
1145 }
1146 else
1147 {
1148 // This will make PerformQuery() go to next column.
1149 // Necessary even in fast mode, as GetNext() will call GetThis() in this case.
1150 maCurPos.first = pColumn->maCells.end();
1151 maCurPos.second = 0;
1152 return false;
1153 }
1154}
1155
1156// Helper that allows binary search of unsorted cells using ScSortedRangeCache.
1157// Rows in the given range are kept in a sorted vector and that vector is binary-searched.
1159{
1160 std::vector<SCROW> mSortedRowsCopy;
1161 const std::vector<SCROW>& mSortedRows;
1166
1167 const std::vector<SCROW>& makeSortedRows( const ScSortedRangeCache* cache, SCROW startRow, SCROW endRow )
1168 {
1169 // Keep a reference to rows from the cache if equal, otherwise make a copy.
1170 if(startRow == cache->getRange().aStart.Row() && endRow == cache->getRange().aEnd.Row())
1171 return cache->sortedRows();
1172 else
1173 {
1174 mSortedRowsCopy.reserve( cache->sortedRows().size());
1175 for( SCROW row : cache->sortedRows())
1176 if( row >= startRow && row <= endRow )
1177 mSortedRowsCopy.emplace_back( row );
1178 return mSortedRowsCopy;
1179 }
1180 }
1181
1182public:
1183 SortedCacheIndexer( const sc::CellStoreType& cells, SCROW startRow, SCROW endRow,
1184 const ScSortedRangeCache* cache )
1185 : mSortedRows( makeSortedRows( cache, startRow, endRow ))
1186 , mCells( cells )
1187 , mValid( false )
1188 {
1189 if(mSortedRows.empty())
1190 {
1191 // coverity[uninit_member] - these are initialized only if valid
1192 return;
1193 }
1194 mLowIndex = 0;
1195 mHighIndex = mSortedRows.size() - 1;
1196 mValid = true;
1197 }
1198
1199 sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
1200 {
1201 // TODO optimize?
1202 SCROW row = mSortedRows[ nIndex ];
1203 return mCells.position(row);
1204 }
1205
1206 BinarySearchCellType getCell( size_t nIndex ) const
1207 {
1208 BinarySearchCellType aRet;
1209 aRet.second = -1;
1210
1211 sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
1212 if (aPos.first == mCells.end())
1213 return aRet;
1214
1215 aRet.first = sc::toRefCell(aPos.first, aPos.second);
1216 aRet.second = aPos.first->position + aPos.second;
1217 return aRet;
1218 }
1219
1220 size_t getLowIndex() const { return mLowIndex; }
1221
1222 size_t getHighIndex() const { return mHighIndex; }
1223
1224 bool isValid() const { return mValid; }
1225};
1226
1229 const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow)
1230{
1231 return SortedCacheIndexer(rCells, nStartRow, nEndRow, sortedCache);
1232}
1233
1234static bool CanBeUsedForSorterCache(ScDocument& rDoc, const ScQueryParam& rParam,
1235 SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
1236 ScInterpreterContext& context)
1237{
1238 if(!rParam.GetEntry(0).bDoQuery || rParam.GetEntry(1).bDoQuery
1239 || rParam.GetEntry(0).GetQueryItems().size() != 1 )
1240 return false;
1242 return false;
1245 return false;
1246 if(!rParam.bByRow)
1247 return false;
1248 if(rParam.bHasHeader)
1249 return false;
1250 if(rParam.mbRangeLookup)
1251 return false;
1253 && !ScQueryEvaluator::isMatchWholeCell(rDoc, rParam.GetEntry(0).eOp))
1254 return false; // substring matching cannot be sorted
1255 if(rParam.GetEntry(0).eOp != SC_LESS && rParam.GetEntry(0).eOp != SC_LESS_EQUAL
1256 && rParam.GetEntry(0).eOp != SC_GREATER && rParam.GetEntry(0).eOp != SC_GREATER_EQUAL
1257 && rParam.GetEntry(0).eOp != SC_EQUAL)
1258 return false;
1259 // For unittests allow inefficient caching, in order for the code to be checked.
1260 static bool inUnitTest = getenv("LO_TESTNAME") != nullptr;
1261 if(refData == nullptr || refData->Ref1.IsRowRel() || refData->Ref2.IsRowRel())
1262 {
1263 // If this is not a range, then a cache is not worth it. If rows are relative, then each
1264 // computation will use a different area, so the cache wouldn't be reused. Tab/cols are
1265 // not a problem, because formula group computations are done for the same tab/col.
1266 if(!inUnitTest)
1267 return false;
1268 }
1269 if(rParam.nRow2 - rParam.nRow1 < 10)
1270 {
1271 if(!inUnitTest)
1272 return false;
1273 }
1274 if( !cell )
1275 return false;
1276 if( !cell->GetCellGroup() || cell->GetCellGroup()->mnLength < 10 )
1277 {
1278 if(!inUnitTest)
1279 return false;
1280 }
1281 // Check that all the relevant caches would be valid (may not be the case when mixing
1282 // numeric and string cells for ByValue lookups).
1283 for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, rParam.nCol1, rParam.nCol2))
1284 {
1285 ScRange aSortedRangeRange( col, rParam.nRow1, nTab, col, rParam.nRow2, nTab);
1286 if( aSortedRangeRange.Contains( cell->aPos ))
1287 return false; // self-referencing, can't create cache
1288 ScSortedRangeCache& cache = rDoc.GetSortedRangeCache( aSortedRangeRange, rParam, &context );
1289 if(!cache.isValid())
1290 return false;
1291 }
1292 return true;
1293}
1294
1295// Generic query implementation.
1296
1298{
1299 getThisResult = true;
1300 return true; // Return from PerformQuery().
1301}
1302
1303template< ScQueryCellIteratorAccess accessType >
1305{
1306 getThisResult = false;
1307 PerformQuery();
1308 return getThisResult;
1309}
1310
1311template< ScQueryCellIteratorAccess accessType >
1313{
1314 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
1315 nCol = maParam.nCol1;
1316 InitPos();
1317 return GetThis();
1318}
1319
1320template< ScQueryCellIteratorAccess accessType >
1322{
1323 IncPos();
1324 if ( nStopOnMismatch )
1325 nStopOnMismatch = nStopOnMismatchEnabled;
1326 if ( nTestEqualCondition )
1327 nTestEqualCondition = nTestEqualConditionEnabled;
1328 return GetThis();
1329}
1330
1331template<>
1333{
1334 assert( !nStopOnMismatch );
1335 assert( !nTestEqualCondition );
1336 // When searching using sorted cache, we should always find cells that match,
1337 // because InitPos()/IncPos() select only such rows, so skip GetThis() (and thus
1338 // the somewhat expensive PerformQuery) as long as we're not at the end
1339 // of a column. As an optimization IncPosFast() returns true if not at the end,
1340 // in which case in non-DBG_UTIL mode it doesn't even bother to set maCurPos.
1341 if( IncPosFast())
1342 {
1343#ifdef DBG_UTIL
1344 assert(GetThis());
1345#endif
1346 return true;
1347 }
1348 return GetThis();
1349}
1350
1352 SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
1353 ScInterpreterContext& context)
1354{
1355 return CanBeUsedForSorterCache(rDoc, rParam, nTab, cell, refData, context);
1356}
1357
1358// Countifs implementation.
1359
1361{
1362 ++countIfCount;
1363 return false; // Continue searching.
1364}
1365
1366template< ScQueryCellIteratorAccess accessType >
1368{
1369 // Keep Entry.nField in iterator on column change
1370 SetAdvanceQueryParamEntryField( true );
1371 assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, FIX IT");
1372 maParam.nCol1 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol1);
1373 maParam.nCol2 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol2);
1374 nCol = maParam.nCol1;
1375 InitPos();
1376 countIfCount = 0;
1377 PerformQuery();
1378 return countIfCount;
1379}
1380
1381
1383 SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
1384 ScInterpreterContext& context)
1385{
1386 return CanBeUsedForSorterCache(rDoc, rParam, nTab, cell, refData, context);
1387}
1388
1389template<>
1391{
1392 // Keep Entry.nField in iterator on column change
1394 assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, FIX IT");
1395 sal_uInt64 count = 0;
1396 // Each column must be sorted separately.
1397 for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, maParam.nCol1, maParam.nCol2))
1398 {
1399 nCol = col;
1400 nRow = maParam.nRow1;
1401 ScRange aSortedRangeRange( col, maParam.nRow1, nTab, col, maParam.nRow2, nTab);
1402 ScQueryOp& op = maParam.GetEntry(0).eOp;
1403 SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext ));
1404 if( op == SC_EQUAL )
1405 {
1406 // BinarySearch() searches for the last item that matches. Therefore first
1407 // find the last non-matching position using SC_LESS and then find the last
1408 // matching position using SC_EQUAL.
1409 ScQueryOp saveOp = op;
1410 op = SC_LESS;
1411 if( BinarySearch( nCol, true ))
1412 {
1413 op = saveOp; // back to SC_EQUAL
1414 size_t lastNonMatching = sortedCache->indexForRow(nRow);
1415 if( BinarySearch( nCol ))
1416 {
1417 size_t lastMatching = sortedCache->indexForRow(nRow);
1418 assert(lastMatching >= lastNonMatching);
1419 count += lastMatching - lastNonMatching;
1420 }
1421 else
1422 {
1423 // BinarySearch() should at least find the same result as the SC_LESS
1424 // call, so this should not happen.
1425 assert(false);
1426 }
1427 }
1428 else
1429 {
1430 // BinarySearch() returning false means that all values are larger,
1431 // so try to find matching ones and count those up to and including
1432 // the found one.
1433 op = saveOp; // back to SC_EQUAL
1434 if( BinarySearch( nCol ))
1435 {
1436 size_t lastMatching = sortedCache->indexForRow(nRow) + 1;
1437 count += lastMatching;
1438 }
1439 else if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
1440 && rDoc.IsEmptyData(col, maParam.nRow1, col, maParam.nRow2, nTab))
1441 {
1442 // BinarySearch() returns false in case it's all empty data,
1443 // handle that specially.
1444 count += maParam.nRow2 - maParam.nRow1 + 1;
1445 }
1446 }
1447 }
1448 else
1449 {
1450 // BinarySearch() searches for the last item that matches. Therefore everything
1451 // up to and including the found row matches the condition.
1452 if( BinarySearch( nCol ))
1453 count += sortedCache->indexForRow(nRow) + 1;
1454 }
1455 }
1456 if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
1457 && maParam.nCol2 >= rDoc.GetAllocatedColumnsCount( nTab ))
1458 {
1459 const sal_uInt64 nRows = maParam.nRow2 - maParam.nRow1 + 1;
1460 count += (maParam.nCol2 - rDoc.GetAllocatedColumnsCount(nTab)) * nRows;
1461 }
1462 return count;
1463}
1464
1469
1470// gcc for some reason needs these too
1475
1476/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
const NodeContext & mrContext
sal_Int32 compareString(const OUString &s1, const OUString &s2) const
SCROW Row() const
Definition: address.hxx:274
static OUString GetInputString(const ScRefCellValue &rCell, sal_uInt32 nFormat, SvNumberFormatter &rFormatter, const ScDocument &rDoc, const svl::SharedString **pShared=nullptr, bool bFiltering=false, bool bForceSystemLocale=false)
Definition: cellform.cxx:129
bool IsEmptyData() const
Definition: column2.cxx:1274
sc::CellStoreType maCells
Definition: column.hxx:196
sal_uInt32 GetNumberFormat(const ScInterpreterContext &rContext, SCROW nRow) const
Definition: column.hxx:979
static bool CanBeUsed(ScDocument &rDoc, const ScQueryParam &aParam, SCTAB nTab, const ScFormulaCell *cell, const ScComplexRefData *refData, ScInterpreterContext &context)
Definition: queryiter.cxx:1382
sal_uInt64 GetCount()
Definition: queryiter.cxx:1367
SC_DLLPUBLIC ScColumnsRange GetAllocatedColumnsRange(SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
Definition: document.cxx:2563
ScSortedRangeCache & GetSortedRangeCache(const ScRange &rRange, const ScQueryParam &param, ScInterpreterContext *pContext)
Definition: documen2.cxx:1247
const ScFormulaCellGroupRef & GetCellGroup() const
double GetValue()
ScAddress aPos
static SC_DLLPUBLIC CollatorWrapper & GetCollator()
case-insensitive collator
Definition: global.cxx:1093
bool BinarySearch(SCCOL col, bool forEqual=false)
Definition: queryiter.cxx:329
ScQueryCellIteratorBase(ScDocument &rDocument, ScInterpreterContext &rContext, SCTAB nTable, const ScQueryParam &aParam, bool bMod)
Definition: queryiter.cxx:63
void AdvanceQueryParamEntryField()
Definition: queryiter.cxx:288
static bool CanBeUsed(ScDocument &rDoc, const ScQueryParam &aParam, SCTAB nTab, const ScFormulaCell *cell, const ScComplexRefData *refData, ScInterpreterContext &context)
Definition: queryiter.cxx:1351
bool FindEqualOrSortedLastInRange(SCCOL &nFoundCol, SCROW &nFoundRow)
In a range assumed to be sorted find either the last of a sequence of equal entries or the last being...
Definition: queryiter.cxx:667
bool isMatchWholeCell(ScQueryOp eOp) const
bool ValidQuery(SCROW nRow, const ScRefCellValue *pCell=nullptr, sc::TableColumnBlockPositionSet *pBlockPos=nullptr)
ScAddress aEnd
Definition: address.hxx:498
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
ScAddress aStart
Definition: address.hxx:497
Sorted cache for one range used with interpreter functions such as VLOOKUP and MATCH.
Definition: rangecache.hxx:45
bool isValid() const
Returns if the cache is usable.
Definition: rangecache.hxx:52
const std::vector< SCROW > & sortedRows() const
Definition: rangecache.hxx:93
const ScRange & getRange() const
Definition: rangecache.hxx:57
const OUString & getString() const
int nCount
@ CELLTYPE_FORMULA
Definition: global.hxx:275
@ CELLTYPE_VALUE
Definition: global.hxx:273
ScQueryOp
Definition: global.hxx:833
@ SC_LESS_EQUAL
Definition: global.hxx:837
@ SC_LESS
Definition: global.hxx:835
@ SC_GREATER_EQUAL
Definition: global.hxx:838
@ SC_GREATER
Definition: global.hxx:836
@ SC_EQUAL
Definition: global.hxx:834
sal_Int32 nIndex
sal_uInt16 nPos
aStr
RttiCompleteObjectLocator col
int i
constexpr std::enable_if< std::is_signed< T1 >::value &&std::is_signed< T2 >::value, bool >::type isInRange(T2 value)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
const mdds::mtv::element_t element_type_edittext
Definition: mtvelements.hxx:48
mdds::mtv::soa::multi_type_vector< CellStoreTraits > CellStoreType
Cell container.
ScRefCellValue toRefCell(const sc::CellStoreType::const_iterator &itPos, size_t nOffset)
const mdds::mtv::element_t element_type_string
Definition: mtvelements.hxx:47
const mdds::mtv::element_t element_type_empty
Definition: mtvelements.hxx:56
size_t mLowIndex
Definition: queryiter.cxx:1163
size_t mnLowIndex
Definition: queryiter.cxx:921
BlockMapType maBlockMap
Definition: queryiter.cxx:917
std::vector< SCROW > mSortedRowsCopy
Definition: queryiter.cxx:1160
const std::vector< SCROW > & mSortedRows
Definition: queryiter.cxx:1161
std::map< size_t, sc::CellStoreType::const_iterator > BlockMapType
Definition: queryiter.cxx:915
bool mValid
Definition: queryiter.cxx:1165
size_t mnHighIndex
Definition: queryiter.cxx:922
const sc::CellStoreType & mCells
Definition: queryiter.cxx:1162
const sc::CellStoreType & mrCells
Definition: queryiter.cxx:919
static bool CanBeUsedForSorterCache(ScDocument &rDoc, const ScQueryParam &rParam, SCTAB nTab, const ScFormulaCell *cell, const ScComplexRefData *refData, ScInterpreterContext &context)
Definition: queryiter.cxx:1234
size_t mHighIndex
Definition: queryiter.cxx:1164
bool mbValid
Definition: queryiter.cxx:924
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
ScSingleRefData Ref2
Definition: refdata.hxx:125
ScSingleRefData Ref1
Definition: refdata.hxx:124
svl::SharedString maString
Definition: queryentry.hxx:49
Each instance of this struct represents a single filtering criteria.
Definition: queryentry.hxx:34
SCCOLROW nField
Definition: queryentry.hxx:61
const Item & GetQueryItem() const
Definition: queryentry.hxx:85
ScQueryOp eOp
Definition: queryentry.hxx:62
QueryItemsType & GetQueryItems()
Definition: queryentry.hxx:75
SC_DLLPUBLIC const ScQueryEntry & GetEntry(SCSIZE n) const
Definition: queryparam.cxx:116
utl::SearchParam::SearchType eSearchType
Definition: queryparam.hxx:43
bool mbRangeLookup
for spreadsheet functions like MATCH, LOOKUP, HLOOKUP, VLOOKUP
Definition: queryparam.hxx:49
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
ScFormulaCell * getFormula() const
Definition: cellvalue.hxx:137
double getDouble() const
Definition: cellvalue.hxx:134
double getValue()
Definition: cellvalue.cxx:629
bool hasString() const
Definition: cellvalue.cxx:614
CellType getType() const
Definition: cellvalue.hxx:133
bool IsRowRel() const
Definition: refdata.hxx:67
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
ScQueryCellIteratorAccess
Definition: types.hxx:145
sal_Int32 SCROW
Definition: types.hxx:17