Files
mars-flaim/sql/src/kybldkey.cpp
dsandersoremutah 021073907f Added .cpp and .h files under the sql/src subdirectory
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@469 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-05-26 23:17:49 +00:00

2002 lines
48 KiB
C++

//------------------------------------------------------------------------------
// Desc: Build from and until keys from a predicate
//
// Tabs: 3
//
// Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id$
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC FLMUINT kyAddInclComponent(
FLMUINT uiNumKeyComponents,
FLMUINT uiKeyComponent,
FLMBYTE * pucKeyEnd,
FLMBOOL bFromKey,
FLMUINT uiMaxSpaceLeft);
FINLINE void flmSetupFirstToLastKey(
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen);
FSTATIC RCODE flmAddNonTextKeyPiece(
SQL_PRED * pPred,
F_INDEX * pIndex,
FLMUINT uiKeyComponent,
F_COLUMN * pColumn,
F_DataVector * pFromSearchKey,
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
F_DataVector * pUntilSearchKey,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMBOOL * pbCanCompareOnKey);
FSTATIC RCODE flmUTF8FindWildcard(
const FLMBYTE * pucValue,
FLMUINT * puiCharPos,
FLMUINT * puiCompareRules);
FSTATIC RCODE flmCountCharacters(
const FLMBYTE * pucValue,
FLMUINT uiValueLen,
FLMUINT uiMaxToCount,
FLMUINT * puiCompareRules,
FLMUINT * puiCount);
FSTATIC RCODE flmSelectBestSubstr(
const FLMBYTE ** ppucValue,
FLMUINT * puiValueLen,
FLMUINT * puiCompareRules,
FLMBOOL * pbTrailingWildcard,
FLMBOOL * pbNotUsingFirstOfString);
FSTATIC void setFromCaseByte(
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
FLMUINT uiCaseLen,
FLMBOOL bIsDBCS,
FLMBOOL bAscending,
FLMBOOL bExcl);
FSTATIC void setUntilCaseByte(
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMUINT uiCaseLen,
FLMBOOL bIsDBCS,
FLMBOOL bAscending,
FLMBOOL bExcl);
FSTATIC RCODE flmAddTextKeyPiece(
SQL_PRED * pPred,
F_INDEX * pIndex,
FLMUINT uiKeyComponent,
F_DataVector * pFromSearchKey,
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
F_DataVector * pUntilSearchKey,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMBOOL * pbCanCompareOnKey);
/****************************************************************************
Desc: Add what is needed to an until key so that it is greater than or equal
for all possible components that come after the primary component.
In other words, this key should be less than any keys whose primary
component is greater than it, but greater than all keys whose primary
component is less than or equal to it.
****************************************************************************/
FSTATIC FLMUINT kyAddInclComponent(
FLMUINT uiNumKeyComponents,
FLMUINT uiKeyComponent,
FLMBYTE * pucKeyEnd,
FLMBOOL bFromKey,
FLMUINT uiMaxSpaceLeft)
{
FLMUINT uiBytesAdded = 0;
// If there is a next key component that would be expected, set it to
// the highest possible value.
if (uiKeyComponent < uiNumKeyComponents)
{
// Must at least be room for a 2 byte length.
if (uiMaxSpaceLeft >= 2)
{
// Need 2nd key component to sort lower if it is the from key
// higher if it is the until key. Note that KEY_LOW_VALUE and
// KEY_HIGH_VALUE always sort lower/higher no matter whether the
// component is ascending or descending.
if (bFromKey)
{
UW2FBA( (FLMUINT16)KEY_LOW_VALUE, pucKeyEnd);
}
else
{
UW2FBA( (FLMUINT16)KEY_HIGH_VALUE, pucKeyEnd);
}
uiBytesAdded = 2;
}
}
else
{
// There are no more key components.
// Output one byte of 0xFF - which should be higher than any
// possible SEN that could be output for document ID and node IDs.
// Only do this for until keys. For from keys, no need to add
// anything, because an empty list of IDs will be be inclusive
// on the from side - it will sort lower, and therefore be inclusive.
if (uiMaxSpaceLeft && !bFromKey)
{
*pucKeyEnd = 0xFF;
uiBytesAdded = 1;
}
}
return( uiBytesAdded);
}
/****************************************************************************
Desc: Setup a first-to-last key for the index.
****************************************************************************/
FINLINE void flmSetupFirstToLastKey(
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen
)
{
UW2FBA( KEY_LOW_VALUE, pucFromKey);
UW2FBA( KEY_HIGH_VALUE, pucUntilKey);
*puiFromKeyLen = 2;
*puiUntilKeyLen = 2;
}
/****************************************************************************
Desc: Add a key piece to the from and until key. Text fields are not
handled in this routine because of their complexity.
Notes: The goal of this code is to build a the collated compound piece
for the 'from' and 'until' key only once instead of twice.
****************************************************************************/
FSTATIC RCODE flmAddNonTextKeyPiece(
SQL_PRED * pPred,
F_INDEX * pIndex,
FLMUINT uiKeyComponent,
F_COLUMN * pColumn,
F_DataVector * pFromSearchKey,
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
F_DataVector * pUntilSearchKey,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMBOOL * pbCanCompareOnKey)
{
RCODE rc = NE_SFLM_OK;
ICD * pIcd = pIndex->pKeyIcds + uiKeyComponent - 1;
FLMUINT uiFromKeyLen = 0;
FLMUINT uiUntilKeyLen = 0;
FLMBYTE * pucFromKeyLenPos = pucFromKey;
FLMBYTE * pucUntilKeyLenPos = pucUntilKey;
FLMBOOL bDataTruncated;
FLMBYTE * pucFromBuf;
FLMUINT uiFromBufLen;
FLMBYTE * pucUntilBuf;
FLMUINT uiUntilBufLen;
FLMBYTE ucFromNumberBuf [FLM_MAX_NUM_BUF_SIZE];
FLMBYTE ucUntilNumberBuf [FLM_MAX_NUM_BUF_SIZE];
FLMUINT uiValue;
FLMINT iValue;
FLMBOOL bNeg;
FLMUINT64 ui64Value;
FLMINT64 i64Value;
FLMUINT uiFromFlags = 0;
FLMUINT uiUntilFlags = 0;
SQL_VALUE * pFromValue;
SQL_VALUE * pUntilValue;
FLMBOOL bInclFrom;
FLMBOOL bInclUntil;
FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE;
F_BufferIStream bufferIStream;
// Leave room for the component length
pucFromKey += 2;
pucUntilKey += 2;
// Handle the presence case here - this is not done in kyCollate.
if (pIcd->uiFlags & ICD_PRESENCE)
{
f_UINT32ToBigEndian( (FLMUINT32)pIcd->uiColumnNum, pucFromKey);
uiFromKeyLen = uiUntilKeyLen = 4;
f_memcpy( pucUntilKey, pucFromKey, uiUntilKeyLen);
}
else if (pIcd->uiFlags & ICD_METAPHONE)
{
if (pPred->eOperator != SQL_APPROX_EQ_OP ||
pPred->pFromValue->eValType != SQL_UTF8_VAL)
{
flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen,
pucUntilKeyLenPos, puiUntilKeyLen);
if (pPred->eOperator != SQL_EXISTS_OP)
{
*pbCanCompareOnKey = FALSE;
}
goto Exit;
}
*pbCanCompareOnKey = FALSE;
// The value type in pPred->pFromValue is SQL_UTF8_VAL, but the
// calling routine should have put the metaphone value into
// pPred->pFromValue->val.uiVal. Sort of weird, but was the
// only way we could evaluate the cost of multiple words in
// the string.
uiFromBufLen = sizeof( ucFromNumberBuf);
if( RC_BAD( rc = FlmUINT2Storage( pPred->pFromValue->val.uiVal,
&uiFromBufLen, ucFromNumberBuf)))
{
goto Exit;
}
pucFromBuf = &ucFromNumberBuf [0];
if (RC_BAD( rc = bufferIStream.open(
(const char *)pucFromBuf, uiFromBufLen)))
{
goto Exit;
}
uiFromKeyLen = SFLM_MAX_KEY_SIZE - 2;
bDataTruncated = FALSE;
// Pass 0 for compare rules because it is non-text
if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen,
&bufferIStream, SFLM_NUMBER_TYPE,
pIcd->uiFlags, 0,
pIcd->uiLimit, NULL, NULL,
pIndex->uiLanguage, FALSE, FALSE,
&bDataTruncated, NULL)))
{
goto Exit;
}
bufferIStream.close();
if (bDataTruncated)
{
// This should never happen on numeric data.
flmAssert( 0);
*pbCanCompareOnKey = FALSE;
}
if (uiFromKeyLen)
{
f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen);
}
uiUntilKeyLen = uiFromKeyLen;
}
else
{
if (pPred->eOperator == SQL_EXISTS_OP ||
pPred->eOperator == SQL_NE_OP ||
pPred->eOperator == SQL_APPROX_EQ_OP)
{
// Setup a first-to-last key
flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen,
pucUntilKeyLenPos, puiUntilKeyLen);
goto Exit;
}
// Only other operator possible is the range operator
flmAssert( pPred->eOperator == SQL_RANGE_OP);
if (bAscending)
{
pFromValue = pPred->pFromValue;
bInclFrom = pPred->bInclFrom;
pUntilValue = pPred->pUntilValue;
bInclUntil = pPred->bInclUntil;
}
else
{
pFromValue = pPred->pUntilValue;
bInclFrom = pPred->bInclUntil;
pUntilValue = pPred->pFromValue;
bInclUntil = pPred->bInclFrom;
}
// Set up from buffer
if (!pFromValue)
{
pucFromBuf = NULL;
uiFromBufLen = 0;
}
else
{
switch (pFromValue->eValType)
{
case SQL_UINT_VAL:
uiValue = pFromValue->val.uiVal;
if (!bInclFrom)
{
if (bAscending)
{
uiValue++;
}
else
{
uiValue--;
}
}
uiFromBufLen = sizeof( ucFromNumberBuf);
if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiFromBufLen,
ucFromNumberBuf)))
{
goto Exit;
}
pucFromBuf = &ucFromNumberBuf [0];
break;
case SQL_INT_VAL:
iValue = pFromValue->val.iVal;
if (!bInclFrom)
{
if (bAscending)
{
iValue++;
}
else
{
iValue--;
}
}
uiFromBufLen = sizeof( ucFromNumberBuf);
if (RC_BAD( rc = FlmINT2Storage( iValue, &uiFromBufLen,
ucFromNumberBuf)))
{
goto Exit;
}
pucFromBuf = &ucFromNumberBuf [0];
break;
case SQL_UINT64_VAL:
ui64Value = pFromValue->val.ui64Val;
if (!bInclFrom)
{
if (bAscending)
{
ui64Value++;
}
else
{
ui64Value--;
}
}
uiFromBufLen = sizeof( ucFromNumberBuf);
if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen,
ucFromNumberBuf, FALSE, FALSE)))
{
goto Exit;
}
pucFromBuf = &ucFromNumberBuf [0];
break;
case SQL_INT64_VAL:
i64Value = pFromValue->val.i64Val;
if (!bInclFrom)
{
if (bAscending)
{
i64Value++;
}
else
{
i64Value--;
}
}
if (i64Value < 0)
{
bNeg = TRUE;
ui64Value = (FLMUINT64)-i64Value;
}
else
{
bNeg = FALSE;
ui64Value = (FLMUINT64)i64Value;
}
uiFromBufLen = sizeof( ucFromNumberBuf);
if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen,
ucFromNumberBuf, bNeg, FALSE)))
{
goto Exit;
}
pucFromBuf = &ucFromNumberBuf [0];
break;
case SQL_BINARY_VAL:
pucFromBuf = pFromValue->val.pucBuf;
uiFromBufLen = pFromValue->uiDataLen;
if (!bInclFrom)
{
// Should use EXCLUSIVE_GT_FLAG even if in descending
// order, because the comparison routines will take
// that into account.
uiFromFlags |= EXCLUSIVE_GT_FLAG;
}
break;
default:
// Text type should have been taken care of elsewhere.
rc = RC_SET_AND_ASSERT( NE_SFLM_QUERY_SYNTAX);
goto Exit;
}
}
// Set up until buffer.
if (!pUntilValue)
{
pucUntilBuf = NULL;
uiUntilBufLen = 0;
}
else if (pUntilValue == pFromValue)
{
pucUntilBuf = pucFromBuf;
uiUntilBufLen = uiFromBufLen;
}
else
{
switch (pUntilValue->eValType)
{
case SQL_UINT_VAL:
uiValue = pUntilValue->val.uiVal;
if (!bInclUntil)
{
if (bAscending)
{
uiValue--;
}
else
{
uiValue++;
}
}
uiUntilBufLen = sizeof( ucUntilNumberBuf);
if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiUntilBufLen,
ucUntilNumberBuf)))
{
goto Exit;
}
pucUntilBuf = &ucUntilNumberBuf [0];
break;
case SQL_INT_VAL:
iValue = pUntilValue->val.iVal;
if (!bInclUntil)
{
if (bAscending)
{
iValue--;
}
else
{
iValue++;
}
}
uiUntilBufLen = sizeof( ucUntilNumberBuf);
if (RC_BAD( rc = FlmINT2Storage( iValue, &uiUntilBufLen,
ucUntilNumberBuf)))
{
goto Exit;
}
pucUntilBuf = &ucUntilNumberBuf [0];
break;
case SQL_UINT64_VAL:
ui64Value = pUntilValue->val.ui64Val;
if (!bInclUntil)
{
if (bAscending)
{
ui64Value--;
}
else
{
ui64Value++;
}
}
uiUntilBufLen = sizeof( ucUntilNumberBuf);
if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen,
ucUntilNumberBuf, FALSE, FALSE)))
{
goto Exit;
}
pucUntilBuf = &ucUntilNumberBuf [0];
break;
case SQL_INT64_VAL:
i64Value = pUntilValue->val.i64Val;
if (!bInclUntil)
{
if (bAscending)
{
i64Value--;
}
else
{
i64Value++;
}
}
if (i64Value < 0)
{
bNeg = TRUE;
ui64Value = (FLMUINT64)-i64Value;
}
else
{
bNeg = FALSE;
ui64Value = (FLMUINT64)i64Value;
}
uiUntilBufLen = sizeof( ucUntilNumberBuf);
if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen,
ucUntilNumberBuf, bNeg, FALSE)))
{
goto Exit;
}
pucUntilBuf = &ucUntilNumberBuf [0];
break;
case SQL_BINARY_VAL:
pucUntilBuf = pUntilValue->val.pucBuf;
uiUntilBufLen = pUntilValue->uiDataLen;
if (!bInclUntil)
{
// Should use EXCLUSIVE_LT_FLAG even if in descending
// order, because the comparison routines will take
// that into account.
uiUntilFlags |= EXCLUSIVE_LT_FLAG;
}
break;
default:
// Text type should have been taken care of elsewhere.
rc = RC_SET_AND_ASSERT( NE_SFLM_QUERY_SYNTAX);
goto Exit;
}
}
// Generate the keys using the from and until buffers that
// have been set up.
if (!pucFromBuf && !pucUntilBuf)
{
// setup a first-to-last key
flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen,
pucUntilKeyLenPos, puiUntilKeyLen);
goto Exit;
}
// Set up the from key
if (!pucFromBuf)
{
uiFromKeyLen = KEY_LOW_VALUE;
}
else
{
if (RC_BAD( rc = bufferIStream.open(
(const char *)pucFromBuf, uiFromBufLen)))
{
goto Exit;
}
uiFromKeyLen = SFLM_MAX_KEY_SIZE - 2;
bDataTruncated = FALSE;
// Pass 0 for compare rules on non-text component.
if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen,
&bufferIStream, pColumn->eDataTyp,
pIcd->uiFlags, 0,
pIcd->uiLimit, NULL, NULL,
pIndex->uiLanguage, FALSE, FALSE,
&bDataTruncated, NULL)))
{
goto Exit;
}
bufferIStream.close();
if (bDataTruncated)
{
*pbCanCompareOnKey = FALSE;
// Save the original data into pFromSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
// Better only be a binary data type at this point.
flmAssert( pFromValue->eValType == SQL_BINARY_VAL);
if (RC_BAD( rc = pFromSearchKey->setBinary( uiKeyComponent - 1,
pucFromBuf, uiFromBufLen)))
{
goto Exit;
}
uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
}
// Set up the until key
if (!pucUntilBuf)
{
uiUntilKeyLen = KEY_HIGH_VALUE;
}
else if (pucUntilBuf == pucFromBuf)
{
if (uiFromKeyLen)
{
f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen);
}
uiUntilKeyLen = uiFromKeyLen;
// The "exclusive" flags better not have been set in this
// case - because this should only be possible if the operator
// was an EQ.
flmAssert( !(uiFromFlags & EXCLUSIVE_GT_FLAG) &&
!(uiUntilFlags & EXCLUSIVE_LT_FLAG));
if (uiFromFlags & SEARCH_KEY_FLAG)
{
// Save the original data into pUntilSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
// Better only be a binary data type at this point.
flmAssert( pUntilValue->eValType == SQL_BINARY_VAL);
if (RC_BAD( rc = pUntilSearchKey->setBinary( uiKeyComponent - 1,
pucUntilBuf, uiUntilBufLen)))
{
goto Exit;
}
uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
}
else
{
if (RC_BAD( rc = bufferIStream.open(
(const char *)pucUntilBuf, uiUntilBufLen)))
{
goto Exit;
}
uiUntilKeyLen = SFLM_MAX_KEY_SIZE - 2;
bDataTruncated = FALSE;
// Pass 0 for compare rule because it is a non-text piece.
if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen,
&bufferIStream, pColumn->eDataTyp,
pIcd->uiFlags, 0,
pIcd->uiLimit, NULL, NULL,
pIndex->uiLanguage, FALSE, FALSE,
&bDataTruncated, NULL)))
{
goto Exit;
}
bufferIStream.close();
if (bDataTruncated)
{
*pbCanCompareOnKey = FALSE;
// Save the original data into pUntilSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
// Better only be a binary data type at this point.
flmAssert( pUntilValue->eValType == SQL_BINARY_VAL);
if (RC_BAD( rc = pUntilSearchKey->setBinary( uiKeyComponent - 1,
pucUntilBuf, uiUntilBufLen)))
{
goto Exit;
}
uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
}
}
UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos);
UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos);
if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < SFLM_MAX_KEY_SIZE - 2)
{
uiFromKeyLen += kyAddInclComponent( pIndex->uiNumKeyComponents, uiKeyComponent,
&pucFromKey [uiFromKeyLen],
TRUE, SFLM_MAX_KEY_SIZE - 2 - uiFromKeyLen);
}
if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < SFLM_MAX_KEY_SIZE - 2)
{
uiUntilKeyLen += kyAddInclComponent( pIndex->uiNumKeyComponents, uiKeyComponent,
&pucUntilKey [uiUntilKeyLen],
FALSE, SFLM_MAX_KEY_SIZE - 2 - uiUntilKeyLen);
}
// Set the FROM and UNTIL key length return values.
if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE)
{
*puiFromKeyLen = uiFromKeyLen + 2;
}
else
{
*puiFromKeyLen = 2;
}
if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE)
{
*puiUntilKeyLen = uiUntilKeyLen + 2;
}
else
{
*puiUntilKeyLen = 2;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Finds the location of a wildcard in the internal text string, if any.
****************************************************************************/
FSTATIC RCODE flmUTF8FindWildcard(
const FLMBYTE * pucValue,
FLMUINT * puiCharPos,
FLMUINT * puiCompareRules)
{
RCODE rc = NE_SFLM_OK;
const FLMBYTE * pucSaveVal;
const FLMBYTE * pucStart = pucValue;
FLMUNICODE uzChar;
FLMUINT uiCompareRules = *puiCompareRules;
flmAssert( pucValue);
*puiCharPos = FLM_MAX_UINT;
for( ;;)
{
pucSaveVal = pucValue;
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar)))
{
goto Exit;
}
if (!uzChar)
{
break;
}
if ((uzChar = f_convertChar( uzChar, uiCompareRules)) == 0)
{
continue;
}
if (uzChar == ASCII_WILDCARD)
{
*puiCharPos = (FLMUINT)(pucSaveVal - pucStart);
goto Exit;
}
if (uzChar != ASCII_SPACE)
{
// Once we hit a non-space character - except for the wildcard,
// we can remove the ignore leading space rule.
uiCompareRules &= (~(FLM_COMP_IGNORE_LEADING_SPACE));
if (uzChar == ASCII_BACKSLASH)
{
// Skip the escaped character
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar)))
{
goto Exit;
}
if (!uzChar)
{
rc = RC_SET( NE_SFLM_Q_BAD_SEARCH_STRING);
goto Exit;
}
}
}
}
Exit:
*puiCompareRules = uiCompareRules;
return( rc);
}
/****************************************************************************
Desc: Count the number of characters that would be returned.
****************************************************************************/
FSTATIC RCODE flmCountCharacters(
const FLMBYTE * pucValue,
FLMUINT uiValueLen,
FLMUINT uiMaxToCount,
FLMUINT * puiCompareRules,
FLMUINT * puiCharCount)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiNumChars = 0;
FLMUINT uiCompareRules = *puiCompareRules;
const FLMBYTE * pucEnd = &pucValue [uiValueLen];
FLMUNICODE uzChar;
FLMBOOL bLastCharWasSpace = FALSE;
FLMUINT uiNumSpaces = 0;
while (uiNumChars < uiMaxToCount)
{
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, pucEnd, &uzChar)))
{
goto Exit;
}
if (!uzChar)
{
if (bLastCharWasSpace)
{
// The spaces are trailing spaces, so if the ignore trailing
// space flag is set, we do nothing. If the compress space
// flag is set, we will increment by one. Otherwise, we will
// add in a count for all of the spaces.
if (!(uiCompareRules & FLM_COMP_IGNORE_TRAILING_SPACE))
{
if (uiCompareRules & FLM_COMP_COMPRESS_WHITESPACE)
{
uiNumChars++;
}
else
{
uiNumChars += uiNumSpaces;
}
}
}
break;
}
if ((uzChar = f_convertChar( uzChar, uiCompareRules)) == 0)
{
continue;
}
if (uzChar == ASCII_SPACE)
{
if (!bLastCharWasSpace)
{
bLastCharWasSpace = TRUE;
uiNumSpaces = 0;
}
uiNumSpaces++;
}
else
{
// Once we hit a non-space character, disable the ignore
// leading space flag.
uiCompareRules &= (~(FLM_COMP_IGNORE_LEADING_SPACE));
if (bLastCharWasSpace)
{
bLastCharWasSpace = FALSE;
if (uiCompareRules & FLM_COMP_COMPRESS_WHITESPACE)
{
// Consecutive spaces are compressed to a single space.
uiNumChars++;
}
else
{
// The spaces were not trailing spaces and were not compressed
// so we need to count all of them.
uiNumChars += uiNumSpaces;
}
}
if (uzChar == ASCII_BACKSLASH)
{
// Skip the next character, no matter what it is - only want
// to count one character here. A backslash followed by any
// character is only a single character.
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, pucEnd, &uzChar)))
{
goto Exit;
}
}
uiNumChars++;
}
}
Exit:
*puiCharCount = uiNumChars;
*puiCompareRules = uiCompareRules;
return( rc);
}
/****************************************************************************
Desc: Select the best substring for a CONTAINS or MATCH_END search.
****************************************************************************/
FSTATIC RCODE flmSelectBestSubstr(
const FLMBYTE ** ppucValue, // [in/out]
FLMUINT * puiValueLen, // [in/out]
FLMUINT * puiCompareRules,
FLMBOOL * pbTrailingWildcard, // [in] change if found a wildcard
FLMBOOL * pbNotUsingFirstOfString)
{
RCODE rc = NE_SFLM_OK;
const FLMBYTE * pucValue = *ppucValue;
const FLMBYTE * pucCurValue;
const FLMBYTE * pucBest;
const FLMBYTE * pucEnd;
const FLMBYTE * pucTmp;
FLMBOOL bBestTerminatesWithWildCard = *pbTrailingWildcard;
FLMUINT uiCurLen;
FLMUINT uiBestNumChars;
FLMUINT uiBestValueLen;
FLMUINT uiWildcardPos;
FLMUINT uiTargetNumChars;
FLMUINT uiNumChars;
FLMBOOL bNotUsingFirstOfString = FALSE;
FLMUNICODE uzChar;
FLMUNICODE uzDummy;
#define GOOD_ENOUGH_CHARS 16
// There may not be any wildcards at all. Find the first one.
if (RC_BAD( rc = flmUTF8FindWildcard( pucValue,
&uiWildcardPos, puiCompareRules)))
{
goto Exit;
}
// FLM_MAX_UINT is returned if no wildcard was found.
if (uiWildcardPos == FLM_MAX_UINT)
{
goto Exit;
}
pucEnd = &pucValue [*puiValueLen];
bBestTerminatesWithWildCard = TRUE;
pucBest = pucValue;
// Skip past the wildcard
pucTmp = &pucValue [uiWildcardPos];
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy)))
{
goto Exit;
}
uiCurLen = *puiValueLen - (FLMUINT)(pucTmp - pucValue);
pucCurValue = pucTmp;
uiBestValueLen = uiWildcardPos;
if (RC_BAD( rc = flmCountCharacters( pucValue, uiWildcardPos,
GOOD_ENOUGH_CHARS, puiCompareRules, &uiBestNumChars)))
{
goto Exit;
}
uiTargetNumChars = uiBestNumChars + uiBestNumChars;
// Here is the great FindADoubleLengthThatIsBetter algorithm.
// Below are the values to pick a next better contains key.
// First Key Size Next Key Size that will be used
// 1 * 2 2 // Single char searches are REALLY BAD
// 2 * 2 4
// 3 * 2 6
// 4 * 2 8
// ... ...
// At each new key piece, increment the target length by 2 so that it
// will be even harder to find a better key.
while (uiBestNumChars < GOOD_ENOUGH_CHARS)
{
pucTmp = pucCurValue;
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzChar)))
{
goto Exit;
}
if (!uzChar)
{
break;
}
if (RC_BAD( rc = flmUTF8FindWildcard( pucCurValue, &uiWildcardPos,
puiCompareRules)))
{
goto Exit;
}
// FLM_MAX_UINT is returned when no wildcard is found.
if (uiWildcardPos == FLM_MAX_UINT)
{
// No wildcard found
// Check the last section that may or may not have trailing *.
if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiCurLen,
GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars)))
{
goto Exit;
}
if (uiNumChars >= uiTargetNumChars)
{
pucBest = pucCurValue;
uiBestValueLen = uiCurLen;
bBestTerminatesWithWildCard = *pbTrailingWildcard;
}
break;
}
else
{
if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiWildcardPos,
GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars)))
{
goto Exit;
}
if (uiNumChars >= uiTargetNumChars)
{
pucBest = pucCurValue;
uiBestValueLen = uiWildcardPos;
uiBestNumChars = uiNumChars;
uiTargetNumChars = uiNumChars + uiNumChars;
}
else
{
uiTargetNumChars += 2;
}
// Skip past the wildcard
pucTmp = &pucCurValue[ uiWildcardPos];
if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy)))
{
goto Exit;
}
uiCurLen -= (FLMUINT)(pucTmp - pucCurValue);
pucCurValue = pucTmp;
}
}
if (pucBest != *ppucValue)
{
bNotUsingFirstOfString = TRUE;
}
*ppucValue = pucBest;
*puiValueLen = uiBestValueLen;
*pbTrailingWildcard = bBestTerminatesWithWildCard;
Exit:
*pbNotUsingFirstOfString = bNotUsingFirstOfString;
return( rc);
}
/****************************************************************************
Desc: Set the case byte on the from key for a case-insensitive search
that is using a case sensitive index.
****************************************************************************/
FSTATIC void setFromCaseByte(
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
FLMUINT uiCaseLen,
FLMBOOL bIsDBCS,
FLMBOOL bAscending,
FLMBOOL bExcl)
{
// Subtract off all but the case marker.
// Remember that for DBCS (Asian) the case marker is two bytes.
*puiFromKeyLen -= (uiCaseLen -
((FLMUINT)(bIsDBCS
? (FLMUINT)2
: (FLMUINT)1)));
if (bExcl)
{
if (bAscending)
{
// Keys are in ascending order:
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// Thus, to exclude all "abc"s on "from" side we need the
// following key:
// key == abc+6 (COLL_MARKER | SC_UPPER) + 1
pucFromKey[ *puiFromKeyLen - 1] = (COLL_MARKER | SC_UPPER);
}
else
{
// Keys are in descending order:
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// Thus, to exclude "abc"s on "from" side we need the
// following key:
// key == abc+4 (COLL_MARKER | SC_LOWER)
pucFromKey[ *puiFromKeyLen - 1] = (COLL_MARKER | SC_LOWER);
}
}
else // Inclusive
{
if (bAscending)
{
// Keys are in ascending order:
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// Thus, to include all "abc"s on "from" side,
// we need the following key:
// key == abc+4 (COLL_MARKER | SC_LOWER)
pucFromKey [*puiFromKeyLen - 1] = COLL_MARKER | SC_LOWER;
}
else
{
// Keys are in descending order:
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// Thus, to include all "abc"s on "from" side we need the
// following key:
// key == abc+6 (COLL_MARKER | SC_UPPER)
pucFromKey [*puiFromKeyLen - 1] = COLL_MARKER | SC_UPPER;
}
}
}
/****************************************************************************
Desc: Set the case byte on the until key for a case-insensitive search
that is using a case sensitive index.
****************************************************************************/
FSTATIC void setUntilCaseByte(
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMUINT uiCaseLen,
FLMBOOL bIsDBCS,
FLMBOOL bAscending,
FLMBOOL bExcl)
{
// Subtract off all but the case marker.
// Remember that for DBCS (Asian) the case marker is two bytes.
*puiUntilKeyLen -= (uiCaseLen -
((FLMUINT)(bIsDBCS
? (FLMUINT)2
: (FLMUINT)1)));
if (bExcl)
{
if (bAscending)
{
// Keys are in ascending order:
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// Thus, to exclude all "abc"s on the "until" side we need
// the following key:
// key == abc+4 (COLL_MARKER | SC_LOWER)
pucUntilKey[ *puiUntilKeyLen - 1] = (COLL_MARKER | SC_LOWER);
}
else
{
// Keys are in descending order:
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// Thus, to exclude all "abc"s on the "until" side we need
// the following key:
// key == abc+6 (COLL_MARKER | SC_UPPER) + 1
pucUntilKey[ *puiUntilKeyLen - 1] = (COLL_MARKER | SC_UPPER);
}
}
else
{
if (bAscending)
{
// Keys are in ascending order:
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// Thus, to get include all "abc"s on the "until" side we need
// the following key:
// key == abc+6 (COLL_MARKER | SC_UPPER)
pucUntilKey [*puiUntilKeyLen - 1] = (COLL_MARKER | SC_UPPER);
}
else
{
// Keys are in descending order:
// "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER)
// "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER)
// Thus, to include all "abc"s on the "until side we need
// the following key:
// key == abc+4 (COLL_MARKER | SC_LOWER)
pucUntilKey [*puiUntilKeyLen - 1] = (COLL_MARKER | SC_LOWER);
}
}
}
/****************************************************************************
Desc: Build a text key.
****************************************************************************/
FSTATIC RCODE flmAddTextKeyPiece(
SQL_PRED * pPred,
F_INDEX * pIndex,
FLMUINT uiKeyComponent,
F_DataVector * pFromSearchKey,
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
F_DataVector * pUntilSearchKey,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMBOOL * pbCanCompareOnKey)
{
RCODE rc = NE_SFLM_OK;
ICD * pIcd = pIndex->pKeyIcds + uiKeyComponent - 1;
FLMUINT uiFromKeyLen = 0;
FLMUINT uiUntilKeyLen = 0;
FLMBYTE * pucFromKeyLenPos = pucFromKey;
FLMBYTE * pucUntilKeyLenPos = pucUntilKey;
FLMUINT uiLanguage = pIndex->uiLanguage;
FLMUINT uiCollationLen = 0;
FLMUINT uiCharCount;
FLMUINT uiCaseLen;
FLMBOOL bOriginalCharsLost = FALSE;
FLMBOOL bIsDBCS = (uiLanguage >= FLM_FIRST_DBCS_LANG &&
uiLanguage <= FLM_LAST_DBCS_LANG)
? TRUE
: FALSE;
FLMBOOL bCaseInsensitive = (FLMBOOL)((pPred->uiCompareRules &
FLM_COMP_CASE_INSENSITIVE)
? TRUE
: FALSE);
FLMBOOL bDoFirstSubstring = (FLMBOOL)((pIcd->uiFlags & ICD_SUBSTRING)
? TRUE
: FALSE);
FLMBOOL bDoMatchBegin = FALSE;
FLMBOOL bTrailingWildcard = FALSE;
const FLMBYTE * pucFromUTF8Buf = NULL;
FLMUINT uiFromBufLen = 0;
const FLMBYTE * pucUntilUTF8Buf = NULL;
FLMUINT uiUntilBufLen = 0;
FLMUINT uiWildcardPos;
FLMBOOL bDataTruncated;
FLMUINT uiFromFlags = 0;
FLMUINT uiUntilFlags = 0;
FLMUINT uiCompareRules;
FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE;
F_BufferIStream bufferIStream;
switch (pPred->eOperator)
{
// The difference between MATCH and EQ_OP is that EQ does
// not support wildcards embedded in the search key.
case SQL_MATCH_OP:
flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL);
pucFromUTF8Buf = pPred->pFromValue->val.pucBuf;
uiFromBufLen = pPred->pFromValue->uiDataLen;
uiCompareRules = pIcd->uiCompareRules;
if (RC_BAD( rc = flmUTF8FindWildcard( pucFromUTF8Buf, &uiWildcardPos,
&uiCompareRules)))
{
goto Exit;
}
// If there is no wildcard, uiWildcardPos will be FLM_MAX_UINT
if (uiWildcardPos != FLM_MAX_UINT)
{
// If wildcard is in position 0, it is NOT
// a match begin.
if (uiWildcardPos)
{
bDoMatchBegin = TRUE;
uiFromBufLen = uiWildcardPos;
}
}
if (!(pIcd->uiFlags & ICD_SUBSTRING))
{
// Index is NOT a substring index
if (!bDoMatchBegin)
{
// Wildcard was at the beginning, will have
// to search the index from first to last
pucFromUTF8Buf = NULL;
}
else
{
bTrailingWildcard = TRUE;
}
}
else
{
FLMBOOL bNotUsingFirstOfString;
// If this is a substring index look for a
// better 'contains' string to search for.
// We don't like "A*BCDEFG" searches.
bTrailingWildcard = bDoMatchBegin;
uiCompareRules = pIcd->uiCompareRules;
if (RC_BAD( rc = flmSelectBestSubstr( &pucFromUTF8Buf,
&uiFromBufLen,
&uiCompareRules, &bTrailingWildcard,
&bNotUsingFirstOfString)))
{
goto Exit;
}
if (bNotUsingFirstOfString)
{
bDoMatchBegin = bTrailingWildcard;
*pbCanCompareOnKey = FALSE;
bDoFirstSubstring = FALSE;
}
else if (bTrailingWildcard)
{
bDoMatchBegin = TRUE;
}
if (RC_BAD( rc = flmCountCharacters( pucFromUTF8Buf,
uiFromBufLen, 2,
&uiCompareRules, &uiCharCount)))
{
goto Exit;
}
// Special case: Single character contains/MEnd in a substr ix.
if (!bIsDBCS && uiCharCount < 2)
{
pucFromUTF8Buf = NULL;
}
}
pucUntilUTF8Buf = pucFromUTF8Buf;
uiUntilBufLen = uiFromBufLen;
break;
case SQL_RANGE_OP:
if (bAscending)
{
if (pPred->pFromValue)
{
flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL);
pucFromUTF8Buf = pPred->pFromValue->val.pucBuf;
uiFromBufLen = pPred->pFromValue->uiDataLen;
}
else
{
// Should have been done up above
// pucFromUTF8Buf = NULL;
// uiFromBufLen = 0;
}
if (pPred->pUntilValue)
{
flmAssert( pPred->pUntilValue->eValType == SQL_UTF8_VAL);
pucUntilUTF8Buf = pPred->pUntilValue->val.pucBuf;
uiUntilBufLen = pPred->pUntilValue->uiDataLen;
}
else
{
// Should have been done up above.
// pucUntilUTF8Buf = NULL;
// uiUntilBufLen = 0;
}
if (!pPred->bInclFrom)
{
uiFromFlags |= EXCLUSIVE_GT_FLAG;
}
if (!pPred->bInclUntil)
{
uiUntilFlags |= EXCLUSIVE_LT_FLAG;
}
}
else
{
if (pPred->pUntilValue)
{
flmAssert( pPred->pUntilValue->eValType == SQL_UTF8_VAL);
pucFromUTF8Buf = pPred->pUntilValue->val.pucBuf;
uiFromBufLen = pPred->pUntilValue->uiDataLen;
}
else
{
// Should have been done up above
// pucFromUTF8Buf = NULL;
// uiFromBufLen = 0;
}
if (pPred->pFromValue)
{
flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL);
pucUntilUTF8Buf = pPred->pFromValue->val.pucBuf;
uiUntilBufLen = pPred->pFromValue->uiDataLen;
}
else
{
// Should have been done up above.
// pucUntilUTF8Buf = NULL;
// uiUntilBufLen = 0;
}
if (!pPred->bInclUntil)
{
uiFromFlags |= EXCLUSIVE_GT_FLAG;
}
if (!pPred->bInclFrom)
{
uiUntilFlags |= EXCLUSIVE_LT_FLAG;
}
}
break;
case SQL_NE_OP:
// Set up to do full index scan.
// Buffers should already be NULL.
// pucFromUTF8Buf = NULL;
// pucUntilUTF8Buf = NULL;
break;
case SQL_APPROX_EQ_OP:
// Set up to do full index scan.
// Buffers should already be NULL
// pucFromUTF8Buf = NULL;
// pucUntilUTF8Buf = NULL;
// Cannot compare on the key if index is upper case,
// even if the bCaseInsensitive flag is set.
if (pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE)
{
*pbCanCompareOnKey = FALSE;
}
break;
default:
// Every predicate should have been converted to one of the above
// cases, or should be handled by another routine.
rc = RC_SET_AND_ASSERT( NE_SFLM_QUERY_SYNTAX);
goto Exit;
}
// If index is case insensitive, but search is case sensitive
// we must NOT do a key match - we would fail things we should
// not be failing.
if ((pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) && !bCaseInsensitive)
{
*pbCanCompareOnKey = FALSE;
}
if (!pucFromUTF8Buf && !pucUntilUTF8Buf)
{
// setup a first-to-last key
flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen,
pucUntilKeyLenPos, puiUntilKeyLen);
goto Exit;
}
pucFromKey += 2;
pucUntilKey += 2;
if (!pucFromUTF8Buf)
{
uiFromKeyLen = KEY_LOW_VALUE;
}
else
{
if (RC_BAD( rc = bufferIStream.open(
(const char *)pucFromUTF8Buf, uiFromBufLen)))
{
goto Exit;
}
// Add ICD_ESC_CHAR to the icd flags because
// the search string must have BACKSLASHES and '*' escaped.
uiFromKeyLen = SFLM_MAX_KEY_SIZE - 2;
bDataTruncated = FALSE;
if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen,
&bufferIStream, SFLM_STRING_TYPE,
pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules,
pIcd->uiLimit,
&uiCollationLen, &uiCaseLen,
uiLanguage, bDoFirstSubstring,
FALSE, &bDataTruncated,
&bOriginalCharsLost)))
{
goto Exit;
}
bufferIStream.close();
if (bDataTruncated)
{
*pbCanCompareOnKey = FALSE;
// Save the original data into pFromSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
if (RC_BAD( rc = pFromSearchKey->setUTF8( uiKeyComponent - 1,
pucFromUTF8Buf, uiFromBufLen)))
{
goto Exit;
}
uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
else if (bOriginalCharsLost)
{
*pbCanCompareOnKey = FALSE;
}
if (pucFromUTF8Buf != pucUntilUTF8Buf)
{
// Handle scenario of a case-sensitive index, but search is
// case-insensitive.
if (uiFromKeyLen &&
(bIsDBCS ||
(!(pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) &&
bCaseInsensitive)))
{
setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen,
bIsDBCS, bAscending,
(uiFromFlags & EXCLUSIVE_GT_FLAG)
? TRUE
: FALSE);
}
}
else
{
// Handle case where from and until buffers are the same.
// This should only be possible in the equality case or match begin
// case, in which cases neither the EXCLUSIVE_LT_FLAG or the
// EXCLUSIVE_GT_FLAG should be set.
flmAssert( uiFromBufLen == uiUntilBufLen);
flmAssert( !(uiFromFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG)));
flmAssert( !(uiUntilFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG)));
if (uiFromFlags & SEARCH_KEY_FLAG)
{
// Save the original data into pUntilSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent - 1,
pucUntilUTF8Buf, uiUntilBufLen)))
{
goto Exit;
}
uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
if (bDoMatchBegin)
{
if (bAscending)
{
// Handle scenario of a case-sensitive index, but search is
// case-insensitive.
if (uiFromKeyLen &&
(bIsDBCS ||
(!(pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) &&
bCaseInsensitive)))
{
setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen,
bIsDBCS, bAscending, FALSE);
}
// From key is set up properly, setup until key.
if (uiCollationLen)
{
f_memcpy( pucUntilKey, pucFromKey, uiCollationLen);
}
// Fill the rest of the until key with high values.
f_memset( &pucUntilKey[ uiCollationLen], 0xFF,
SFLM_MAX_KEY_SIZE - uiCollationLen - 2);
uiUntilKeyLen = SFLM_MAX_KEY_SIZE - 2;
}
else
{
// Copy from key into until key.
if (uiFromKeyLen)
{
f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen);
}
uiUntilKeyLen = uiFromKeyLen;
if (uiUntilKeyLen &&
(bIsDBCS ||
(!(pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) &&
bCaseInsensitive)))
{
// NOTE: Always inclusive because this is a matchbegin.
setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen,
bIsDBCS, bAscending, FALSE);
}
// Fill rest of from key with high values after collation values.
f_memset( &pucFromKey[ uiCollationLen], 0xFF,
SFLM_MAX_KEY_SIZE - uiCollationLen - 2);
uiFromKeyLen = SFLM_MAX_KEY_SIZE - 2;
}
}
else
{
// Copy from key into until key.
if (!uiFromKeyLen)
{
uiUntilKeyLen = 0;
}
else
{
if (!bDoFirstSubstring)
{
f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen);
uiUntilKeyLen = uiFromKeyLen;
}
else if (bAscending)
{
// Do two copies so that the first substring byte is gone
// in the until key.
f_memcpy( pucUntilKey, pucFromKey, uiCollationLen);
uiUntilKeyLen = uiCollationLen;
if (bIsDBCS)
{
uiCollationLen++;
}
uiCollationLen++;
f_memcpy( &pucUntilKey [uiUntilKeyLen],
pucFromKey + uiCollationLen,
uiFromKeyLen - uiCollationLen);
uiUntilKeyLen += (uiFromKeyLen - uiCollationLen);
}
else
{
// Descending order - put the string without the
// first-substring-marker into the from key instead of
// the until key.
f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen);
uiUntilKeyLen = uiFromKeyLen;
// Modify from key to NOT have first-substring-marker.
f_memcpy( pucUntilKey, pucFromKey, uiCollationLen);
uiFromKeyLen = uiCollationLen;
if (bIsDBCS)
{
uiCollationLen++;
}
uiCollationLen++;
f_memcpy( &pucFromKey [uiFromKeyLen],
pucUntilKey + uiCollationLen,
uiUntilKeyLen - uiCollationLen);
uiFromKeyLen += (uiUntilKeyLen - uiCollationLen);
}
// Handle scenario of a case-sensitive index, but search is
// case-insensitive.
if (bIsDBCS ||
(!(pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) &&
bCaseInsensitive))
{
setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen,
bIsDBCS, bAscending, FALSE);
setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen,
bIsDBCS, bAscending, FALSE);
}
}
}
}
}
// Do the until key now
if (!pucUntilUTF8Buf)
{
uiUntilKeyLen = KEY_HIGH_VALUE;
}
else if (pucFromUTF8Buf != pucUntilUTF8Buf)
{
if (RC_BAD( rc = bufferIStream.open(
(const char *)pucUntilUTF8Buf, uiUntilBufLen)))
{
goto Exit;
}
// Add ICD_ESC_CHAR to the icd flags because
// the search string must have BACKSLASHES and '*' escaped.
uiUntilKeyLen = SFLM_MAX_KEY_SIZE - 2;
bDataTruncated = FALSE;
if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen,
&bufferIStream, SFLM_STRING_TYPE,
pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules,
pIcd->uiLimit,
&uiCollationLen, &uiCaseLen,
uiLanguage, bDoFirstSubstring,
FALSE, &bDataTruncated,
&bOriginalCharsLost)))
{
goto Exit;
}
bufferIStream.close();
if (bDataTruncated)
{
// Save the original data into pUntilSearchKey so the comparison
// routines can do a comparison on the full value if
// necessary.
if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent - 1,
pucUntilUTF8Buf, uiUntilBufLen)))
{
goto Exit;
}
*pbCanCompareOnKey = FALSE;
uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG);
}
else if (bOriginalCharsLost)
{
*pbCanCompareOnKey = FALSE;
}
if (uiUntilKeyLen &&
(bIsDBCS ||
(!(pIcd->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) &&
bCaseInsensitive)))
{
setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen,
bIsDBCS, bAscending,
(uiUntilFlags & EXCLUSIVE_LT_FLAG)
? TRUE
: FALSE);
}
}
UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos);
UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos);
if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < SFLM_MAX_KEY_SIZE - 2)
{
uiFromKeyLen += kyAddInclComponent( pIndex->uiNumKeyComponents, uiKeyComponent,
&pucFromKey [uiFromKeyLen],
TRUE, SFLM_MAX_KEY_SIZE - 2 - uiFromKeyLen);
}
if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < SFLM_MAX_KEY_SIZE - 2)
{
uiUntilKeyLen += kyAddInclComponent( pIndex->uiNumKeyComponents, uiKeyComponent,
&pucUntilKey [uiUntilKeyLen],
FALSE, SFLM_MAX_KEY_SIZE - 2 - uiUntilKeyLen);
}
// Set the FROM and UNTIL key lengths
if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE)
{
*puiFromKeyLen = uiFromKeyLen + 2;
}
else
{
*puiFromKeyLen = 2;
}
if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE)
{
*puiUntilKeyLen = uiUntilKeyLen + 2;
}
else
{
*puiUntilKeyLen = 2;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Build the from and until keys given a field list with operators and
values and an index.
Notes: The knowledge of query definitions is limited in these routines.
****************************************************************************/
RCODE flmBuildFromAndUntilKeys(
F_Dict * pDict,
F_INDEX * pIndex,
F_TABLE * pTable,
SQL_PRED * pPred,
F_DataVector * pFromSearchKey,
FLMBYTE * pucFromKey,
FLMUINT * puiFromKeyLen,
F_DataVector * pUntilSearchKey,
FLMBYTE * pucUntilKey,
FLMUINT * puiUntilKeyLen,
FLMBOOL * pbDoRowMatch,
FLMBOOL * pbCanCompareOnKey)
{
RCODE rc = NE_SFLM_OK;
*puiFromKeyLen = *puiUntilKeyLen = 0;
*pbDoRowMatch = FALSE;
*pbCanCompareOnKey = TRUE;
if (!pPred)
{
// Setup a first-to-last key
flmSetupFirstToLastKey( pucFromKey, puiFromKeyLen,
pucUntilKey, puiUntilKeyLen);
*pbDoRowMatch = TRUE;
*pbCanCompareOnKey = FALSE;
}
else
{
F_COLUMN * pColumn;
ICD * pIcd;
// Predicates we are looking at should NEVER be notted. They
// will have been weeded out earlier.
flmAssert( !pPred->bNotted);
// Handle special cases for indexing presence and/or exists predicate.
pIcd = pIndex->pKeyIcds;
pColumn = pDict->getColumn( pTable, pIcd->uiColumnNum);
if (pColumn->eDataTyp == SFLM_STRING_TYPE &&
!(pIcd->uiFlags & (ICD_PRESENCE | ICD_METAPHONE)) &&
pPred->eOperator != SQL_EXISTS_OP)
{
if (RC_BAD( rc = flmAddTextKeyPiece( pPred, pIndex, 1,
pFromSearchKey, pucFromKey, puiFromKeyLen,
pUntilSearchKey, pucUntilKey, puiUntilKeyLen,
pbCanCompareOnKey)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = flmAddNonTextKeyPiece( pPred, pIndex, 1, pColumn,
pFromSearchKey, pucFromKey, puiFromKeyLen,
pUntilSearchKey, pucUntilKey, puiUntilKeyLen,
pbCanCompareOnKey)))
{
goto Exit;
}
}
}
Exit:
if (!(*pbCanCompareOnKey))
{
*pbDoRowMatch = TRUE;
}
return( rc);
}