sc/inc/lookupcache.hxx | 8 ++++ sc/source/core/tool/interpr1.cxx | 58 ++++++++++++++++++++++++++++++++++++ sc/source/core/tool/lookupcache.cxx | 13 ++++++++ 3 files changed, 78 insertions(+), 1 deletion(-)
New commits: commit 9e55c8c7ffc43004ca314edcd4712dcc29adf933 Author: Serge Krot <serge.k...@cib.de> AuthorDate: Wed Oct 31 22:11:31 2018 +0100 Commit: Eike Rathke <er...@redhat.com> CommitDate: Thu Nov 22 21:24:55 2018 +0100 tdf#121052 sc: avoid multiple empty value lookups in ranges Change-Id: I7759aef51af2f400f3f5ec69854fd9133e845f49 Reviewed-on: https://gerrit.libreoffice.org/62712 Tested-by: Jenkins Reviewed-by: Eike Rathke <er...@redhat.com> diff --git a/sc/inc/lookupcache.hxx b/sc/inc/lookupcache.hxx index 3a2be452a2ee..ea657c67d787 100644 --- a/sc/inc/lookupcache.hxx +++ b/sc/inc/lookupcache.hxx @@ -104,6 +104,10 @@ public: (mbString ? (*mpStr == *r.mpStr) : (mfVal == r.mfVal)); } + bool isEmptyStringQuery() const + { + return (getQueryOp() == QueryOp::EQUAL) && mbString && mpStr && mpStr->isEmpty(); + } }; /// MUST be new'd because Notify() deletes. @@ -112,11 +116,13 @@ public: /// Remove from document structure and delete (!) cache on modify hint. virtual void Notify( const SfxHint& rHint ) override; - /// @returns document address in o_rAddress if Result==FOUND + /// @returns document address in o_rResultAddress if Result==FOUND Result lookup( ScAddress & o_rResultAddress, const QueryCriteria & rCriteria, const ScAddress & rQueryAddress ) const; + SCROW lookup( const QueryCriteria & rCriteria ) const; + /** Insert query and result. @param bAvailable Pass sal_False if the search didn't deliver a result. A subsequent diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx index 14c57288ecef..b1bca830b582 100644 --- a/sc/source/core/tool/interpr1.cxx +++ b/sc/source/core/tool/interpr1.cxx @@ -9763,6 +9763,46 @@ static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc, const return bFound; } +// tdf#121052: +// =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted) +// [SearchCriterion] is the value searched for in the first column of the array. +// [RangeArray] is the reference, which is to comprise at least two columns. +// [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1. +// +// lcl_getPrevRowWithEmptyValueLookup() performs following checks: +// - if value referenced by [SearchCriterion] is empty +// - and if we run query with "exact match" mode (i.e. VLOOKUP) +// - and if we already have the same lookup done before but for another row +// which is also had empty [SearchCriterion] +// +// then +// we could say, that for current row we could reuse results of the cached call which was done for the row2 +// In this case we return row index, which is >= 0. +// +// Elsewhere +// -1 is returned, which will lead to default behavior => +// complete lookup will be done in RangeArray inside lcl_LookupQuery() method. +// +// This method was added only for speed up to avoid several useless complete +// lookups inside [RangeArray] for searching empty strings. +// +static SCROW lcl_getPrevRowWithEmptyValueLookup(const ScLookupCache& rCache, const ScLookupCache::QueryCriteria& aCriteria, const ScQueryParam & rParam) +{ + // is search with equal match? + if (! aCriteria.isEmptyStringQuery()) + return -1; // not found + + // is lookup value empty? + const ScQueryEntry& rEntry = rParam.GetEntry(0); + const ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); + if (! rItem.maString.getString().isEmpty()) + return -1; // not found + + // try to find the row index for which we have already performed lookup + // and have some result of it inside cache + return rCache.lookup( aCriteria ); +} + bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, const ScQueryParam & rParam ) const { @@ -9786,6 +9826,24 @@ bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, ScLookupCache::QueryCriteria aCriteria( rEntry); ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPos); + + // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells + // This check was added only for speed up to avoid several useless complete + // lookups inside [RangeArray] for searching empty strings. + if (eCacheResult == ScLookupCache::NOT_CACHED) + { + const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam); + if (nPrevRowWithEmptyValueLookup >= 0) + { + // make the same lookup using cache with different row index + // (this lookup was already cached) + ScAddress aPosPrev(aPos); + aPosPrev.SetRow(nPrevRowWithEmptyValueLookup); + + eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev ); + } + } + switch (eCacheResult) { case ScLookupCache::NOT_CACHED : diff --git a/sc/source/core/tool/lookupcache.cxx b/sc/source/core/tool/lookupcache.cxx index 9522b46678fe..5e7890aa2421 100644 --- a/sc/source/core/tool/lookupcache.cxx +++ b/sc/source/core/tool/lookupcache.cxx @@ -95,6 +95,19 @@ ScLookupCache::Result ScLookupCache::lookup( ScAddress & o_rResultAddress, return FOUND; } +SCROW ScLookupCache::lookup( const QueryCriteria & rCriteria ) const +{ + // try to find the row index for which we have already performed lookup + for (auto it = maQueryMap.begin(); it != maQueryMap.end(); ++it) + { + if (it->second.maCriteria == rCriteria) + return it->first.mnRow; + } + + // not found + return -1; +} + bool ScLookupCache::insert( const ScAddress & rResultAddress, const QueryCriteria & rCriteria, const ScAddress & rQueryAddress, const bool bAvailable ) _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits