Kaydet (Commit) f0701470 authored tarafından Eike Rathke's avatar Eike Rathke

resolved fdo#71589 reimplemented horizontal range lookup

Regression introduced with ebdd9c30
(first eaea417b) that removed the
bMixedComparison member from ScQueryParam under the false assumption
that is was only used to emulate a legacy Excel behavior. In fact it was
also needed to do the at least horizontal range lookup in sorted mixed
data, though didn't evaluate exactly the same conditions as Excel and
defined in ODFF.

Reimplemented a similar behavior for the new code structures but this
time also checking for the additional condtion that a query ByString
does not return the last numeric result and vice versa, which previously
was missing.

Change-Id: I46061777879ba5301bfcaca2d50cf87a994f93f2
üst a9e7f34c
......@@ -42,6 +42,7 @@ struct ScQueryParamBase
bool bCaseSens;
bool bRegExp;
bool bDuplicate;
bool mbRangeLookup; ///< for spreadsheet functions like MATCH, LOOKUP, HLOOKUP, VLOOKUP
virtual ~ScQueryParamBase();
......
......@@ -1245,6 +1245,17 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
SCROW& nFoundRow, bool bSearchForEqualAfterMismatch,
bool bIgnoreMismatchOnLeadingStringsP )
{
// Set and automatically reset mpParam->mbRangeLookup when returning. We
// could use comphelper::FlagRestorationGuard, but really, that one is
// overengineered for this simple purpose here.
struct BoolResetter
{
bool& mr;
bool mb;
BoolResetter( bool& r, bool b ) : mr(r), mb(r) { r = b; }
~BoolResetter() { mr = mb; }
} aRangeLookupResetter( mpParam->mbRangeLookup, true);
nFoundCol = MAXCOL+1;
nFoundRow = MAXROW+1;
SetStopOnMismatch( true ); // assume sorted keys
......@@ -1253,7 +1264,22 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
bool bRegExp = mpParam->bRegExp && mpParam->GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString;
bool bBinary = !bRegExp && mpParam->bByRow && (mpParam->GetEntry(0).eOp ==
SC_LESS_EQUAL || mpParam->GetEntry(0).eOp == SC_GREATER_EQUAL);
if (bBinary ? (BinarySearch() ? GetThis() : 0) : GetFirst())
bool bFound = false;
if (bBinary)
{
if (BinarySearch())
{
// BinarySearch() already positions correctly and only needs real
// query comparisons afterwards, skip the verification check below.
mpParam->mbRangeLookup = false;
bFound = GetThis();
}
}
else
{
bFound = GetFirst();
}
if (bFound)
{
// First equal entry or last smaller than (greater than) entry.
PositionType aPosSave;
......@@ -1272,9 +1298,43 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
{
// Step back to last in range and adjust position markers for
// GetNumberFormat() or similar.
SCCOL nColDiff = nCol - nFoundCol;
nCol = nFoundCol;
nRow = nFoundRow;
maCurPos = aPosSave;
if (mpParam->mbRangeLookup)
{
// Verify that the found entry does not only fulfill the range
// lookup but also the real query, i.e. not numeric was found
// if query is ByString and vice versa.
mpParam->mbRangeLookup = false;
// Step back the last field advance if GetNext() did one.
if (bAdvanceQuery && nColDiff)
{
SCSIZE nEntries = mpParam->GetEntryCount();
for (SCSIZE j=0; j < nEntries; ++j)
{
ScQueryEntry& rEntry = mpParam->GetEntry( j );
if (rEntry.bDoQuery)
{
if (rEntry.nField - nColDiff >= 0)
rEntry.nField -= nColDiff;
else
{
assert(!"FindEqualOrSortedLastInRange: rEntry.nField -= nColDiff < 0");
}
}
else
break; // for
}
}
// Check it.
if (!GetThis())
{
nFoundCol = MAXCOL+1;
nFoundRow = MAXROW+1;
}
}
}
}
if ( IsEqualConditionFulfilled() )
......
......@@ -1513,6 +1513,41 @@ public:
return std::pair<bool,bool>(bOk, bTestEqual);
}
// To be called only if both isQueryByValue() and isQueryByString()
// returned false and range lookup is wanted! In range lookup comparison
// numbers are less than strings. Nothing else is compared.
std::pair<bool,bool> compareByRangeLookup(
const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
{
bool bTestEqual = false;
if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
return std::pair<bool,bool>(false, bTestEqual);
if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
return std::pair<bool,bool>(false, bTestEqual);
if (!rCell.isEmpty())
{
if (rItem.meType == ScQueryEntry::ByString)
{
if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
// Error values are compared as string.
return std::pair<bool,bool>(false, bTestEqual);
return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
}
return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
}
if (rItem.meType == ScQueryEntry::ByString)
return std::pair<bool,bool>(mrTab.HasValueData(nCol, nRow), bTestEqual);
return std::pair<bool,bool>(!mrTab.HasValueData(nCol, nRow), bTestEqual);
}
};
}
......@@ -1578,6 +1613,13 @@ bool ScTable::ValidQuery(
aRes.first |= aThisRes.first;
aRes.second |= aThisRes.second;
}
else if (rParam.mbRangeLookup)
{
std::pair<bool,bool> aThisRes =
aEval.compareByRangeLookup(aCell, nCol, nRow, rEntry, *itr);
aRes.first |= aThisRes.first;
aRes.second |= aThisRes.second;
}
if (aRes.first && aRes.second)
break;
......
......@@ -64,7 +64,8 @@ ScQueryParamBase::ScQueryParamBase() :
bInplace(true),
bCaseSens(false),
bRegExp(false),
bDuplicate(false)
bDuplicate(false),
mbRangeLookup(false)
{
for (size_t i = 0; i < MAXQUERY; ++i)
maEntries.push_back(new ScQueryEntry);
......@@ -72,7 +73,7 @@ ScQueryParamBase::ScQueryParamBase() :
ScQueryParamBase::ScQueryParamBase(const ScQueryParamBase& r) :
bHasHeader(r.bHasHeader), bByRow(r.bByRow), bInplace(r.bInplace), bCaseSens(r.bCaseSens),
bRegExp(r.bRegExp), bDuplicate(r.bDuplicate),
bRegExp(r.bRegExp), bDuplicate(r.bDuplicate), mbRangeLookup(r.mbRangeLookup),
maEntries(r.maEntries)
{
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment