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