Files
mars-flaim/xflaim/src/kybuild.cpp
ahodgkinson 0e2ba5f0b3 Updated support for AIX.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@729 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-08-01 19:31:50 +00:00

3297 lines
69 KiB
C++

//------------------------------------------------------------------------------
// Desc: This file contains the main routines for building of index keys,
// and adding them to the database.
//
// Tabs: 3
//
// Copyright (c) 1990-1992, 1994-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: kybuild.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
#define STACK_DATA_BUF_SIZE 64
typedef struct NodeTraverseTag * NODE_TRAV_p;
typedef struct AnchorNodeTag * ANCHOR_NODE_p;
typedef struct AnchorNodeTag
{
FLMUINT64 ui64AnchorNodeId;
FLMUINT uiAnchorNameId;
eDomNodeType eNodeType;
FLMBOOL bSeeIfRepeatingSibs;
ANCHOR_NODE_p pNext;
} ANCHOR_NODE;
typedef struct NodeTraverseTag
{
ANCHOR_NODE * pAnchorNode;
F_DOMNode * pNode;
FLMBOOL bInNodeSubtree;
ICD * pIcd;
FLMUINT uiSibIcdAttrs;
FLMUINT uiSibIcdElms;
FLMBOOL bTraverseChildren;
FLMBOOL bTraverseSibs;
NODE_TRAV_p pParent;
NODE_TRAV_p pChild;
} NODE_TRAV;
// Local function prototypes
FSTATIC RCODE kyAddIDsToKey(
FLMUINT64 ui64DocumentID,
IXD * pIxd,
CDL_HDR * pCdlTbl,
FLMBYTE * pucKeyBuf,
FLMUINT uiIDBufSize,
FLMUINT * puiIDLen);
FSTATIC RCODE kySeeIfRepeatingSibs(
F_Db * pDb,
F_DOMNode * pNode,
FLMBOOL * pbHadRepeatingSib);
FSTATIC RCODE kyFindChildNode(
F_Db * pDb,
F_Pool * pPool,
NODE_TRAV ** ppTrav,
FLMBOOL * pbGotChild,
FLMBOOL * pbHadRepeatingSib);
FSTATIC RCODE kyFindSibNode(
F_Db * pDb,
NODE_TRAV * pTrav,
FLMBOOL bTestFirstNode,
FLMBOOL * pbGotSib,
FLMBOOL * pbHadRepeatingSib);
/****************************************************************************
Desc: Append node IDs to the key buffer.
****************************************************************************/
FSTATIC RCODE kyAddIDsToKey(
FLMUINT64 ui64DocumentID,
IXD * pIxd,
CDL_HDR * pCdlTbl,
FLMBYTE * pucKeyBuf,
FLMUINT uiIDBufSize,
FLMUINT * puiIDLen)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucIDs = pucKeyBuf;
FLMUINT uiSenLen;
FLMUINT uiIDLen = 0;
ICD * pIcd;
FLMUINT64 ui64Id;
FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE];
FLMBYTE * pucTmpSen;
// Our ID buffer can never exceed MAX_ID_SIZE bytes (which is always
// defined to be 256), because it has a trailing
// length byte that we put on it - which can only represent up to
// 255 bytes.
if (uiIDBufSize > MAX_ID_SIZE)
{
uiIDBufSize = MAX_ID_SIZE;
}
// Put document ID into buffer. If there is room for at least nine
// bytes, we can encode the ID right into the buffer safely. Otherwise,
// we have to use a temporary buffer and see if there is room.
if (uiIDBufSize - uiIDLen >= 9)
{
uiIDLen += f_encodeSEN( ui64DocumentID, &pucIDs);
}
else
{
pucTmpSen = &ucTmpSen [0];
uiSenLen = f_encodeSEN( ui64DocumentID, &pucTmpSen);
if (uiSenLen + uiIDLen > uiIDBufSize)
{
rc = RC_SET( NE_XFLM_KEY_OVERFLOW);
goto Exit;
}
f_memcpy( pucIDs, ucTmpSen, uiSenLen);
uiIDLen += uiSenLen;
pucIDs += uiSenLen;
}
// Append the key component NODE IDs to the key
for (pIcd = pIxd->pFirstKey; pIcd; pIcd = pIcd->pNextKeyComponent)
{
ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList &&
pCdlTbl [pIcd->uiCdl].pCdlList->pNode
? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId()
: (FLMUINT64)0);
// Put node ID into buffer. If there is room for at least nine
// bytes, we can encode the ID right into the buffer safely. Otherwise,
// we have to use a temporary buffer and see if there is room.
if (uiIDBufSize - uiIDLen >= 9)
{
uiIDLen += f_encodeSEN( ui64Id, &pucIDs);
}
else
{
pucTmpSen = &ucTmpSen [0];
uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen);
if (uiSenLen + uiIDLen > uiIDBufSize)
{
rc = RC_SET( NE_XFLM_KEY_OVERFLOW);
goto Exit;
}
f_memcpy( pucIDs, ucTmpSen, uiSenLen);
uiIDLen += uiSenLen;
pucIDs += uiSenLen;
}
}
// Append the data NODE IDs to the key
for (pIcd = pIxd->pFirstData; pIcd; pIcd = pIcd->pNextDataComponent)
{
ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList &&
pCdlTbl [pIcd->uiCdl].pCdlList->pNode
? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId()
: (FLMUINT64)0);
// Put node ID into buffer. If there is room for at least nine
// bytes, we can encode the ID right into the buffer safely. Otherwise,
// we have to use a temporary buffer and see if there is room.
if (uiIDBufSize - uiIDLen >= 9)
{
uiIDLen += f_encodeSEN( ui64Id, &pucIDs);
}
else
{
pucTmpSen = &ucTmpSen [0];
uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen);
if (uiSenLen + uiIDLen > uiIDBufSize)
{
rc = RC_SET( NE_XFLM_KEY_OVERFLOW);
goto Exit;
}
f_memcpy( pucIDs, ucTmpSen, uiSenLen);
uiIDLen += uiSenLen;
pucIDs += uiSenLen;
}
}
// Append the context NODE IDs to the key
for (pIcd = pIxd->pFirstContext; pIcd; pIcd = pIcd->pNextKeyComponent)
{
ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList &&
pCdlTbl [pIcd->uiCdl].pCdlList->pNode
? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId()
: (FLMUINT64)0);
// Put node ID into buffer. If there is room for at least nine
// bytes, we can encode the ID right into the buffer safely. Otherwise,
// we have to use a temporary buffer and see if there is room.
if (uiIDBufSize - uiIDLen >= 9)
{
uiIDLen += f_encodeSEN( ui64Id, &pucIDs);
}
else
{
pucTmpSen = &ucTmpSen [0];
uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen);
if (uiSenLen + uiIDLen > uiIDBufSize)
{
rc = RC_SET( NE_XFLM_KEY_OVERFLOW);
goto Exit;
}
f_memcpy( pucIDs, ucTmpSen, uiSenLen);
uiIDLen += uiSenLen;
pucIDs += uiSenLen;
}
}
*puiIDLen = uiIDLen;
Exit:
return( rc);
}
/*****************************************************************************
Desc: F_OldNodeList destructor
*****************************************************************************/
F_OldNodeList::~F_OldNodeList()
{
if (m_pNodeList)
{
f_free( &m_pNodeList);
}
m_pool.poolFree();
}
/*****************************************************************************
Desc: Find a node in the old node list.
*****************************************************************************/
FLMBOOL F_OldNodeList::findNodeInList(
eDomNodeType eNodeType,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
FLMUINT uiNameId,
FLMBYTE ** ppucData,
FLMUINT * puiDataLen,
FLMUINT * puiInsertPos)
{
FLMBOOL bFound = FALSE;
FLMUINT uiTblSize;
FLMUINT uiLow;
FLMUINT uiMid;
FLMUINT uiHigh;
eDomNodeType eTblNodeType;
FLMUINT uiTblCollection;
FLMUINT uiTblNameId;
FLMUINT64 ui64TblNodeId;
// Do binary search in the table
if ((uiTblSize = m_uiNodeCount) == 0)
{
*puiInsertPos = 0;
goto Exit;
}
uiHigh = --uiTblSize;
uiLow = 0;
for (;;)
{
uiMid = (uiLow + uiHigh) / 2;
eTblNodeType = m_pNodeList[ uiMid].eNodeType;
uiTblCollection = m_pNodeList[ uiMid].uiCollection;
ui64TblNodeId = m_pNodeList[ uiMid].ui64NodeId;
uiTblNameId = m_pNodeList[ uiMid].uiNameId;
flmAssert( eTblNodeType != INVALID_NODE);
flmAssert( uiTblCollection);
flmAssert( ui64TblNodeId);
flmAssert( uiTblNameId);
if( eTblNodeType == eNodeType &&
uiTblCollection == uiCollection &&
ui64TblNodeId == ui64NodeId &&
uiTblNameId == uiNameId)
{
bFound = TRUE;
*ppucData = m_pNodeList [uiMid].pucData;
*puiDataLen = m_pNodeList [uiMid].uiDataLen;
*puiInsertPos = uiMid;
goto Exit;
}
// Check if we are done
if( uiLow >= uiHigh)
{
// Done, item not found
if( eNodeType >= eTblNodeType)
{
goto CmpGreaterEq;
}
if( uiCollection >= uiTblCollection)
{
goto CmpGreaterEq;
}
if( ui64NodeId >= ui64TblNodeId)
{
goto CmpGreaterEq;
}
if( uiNameId >= uiTblNameId)
{
CmpGreaterEq:
*puiInsertPos = uiMid + 1;
goto Exit;
}
*puiInsertPos = uiMid;
goto Exit;
}
if( eNodeType >= eTblNodeType)
{
goto CmpGreaterEq2;
}
if( uiCollection >= uiTblCollection)
{
goto CmpGreaterEq2;
}
if( ui64NodeId >= ui64TblNodeId)
{
goto CmpGreaterEq2;
}
if( uiNameId >= uiTblNameId)
{
CmpGreaterEq2:
if (uiMid == uiTblSize)
{
*puiInsertPos = uiMid + 1;
goto Exit;
}
uiLow = uiMid + 1;
continue;
}
if (uiMid == 0)
{
*puiInsertPos = 0;
goto Exit;
}
uiHigh = uiMid - 1;
continue;
}
Exit:
return( bFound);
}
/*****************************************************************************
Desc: Add an old node to the old node list.
*****************************************************************************/
RCODE F_OldNodeList::addNodeToList(
F_Db * pDb,
F_DOMNode * pNode)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiInsertPos;
FLMBYTE * pucData;
FLMUINT uiDataType;
FLMUINT uiDataLen;
FLMUINT uiChars;
FLMUINT uiBufSize;
FLMUINT uiCollection;
FLMUINT uiNameId;
FLMUINT64 ui64NodeId;
if( RC_BAD( rc = pNode->getCollection( pDb, &uiCollection)))
{
goto Exit;
}
if( RC_BAD( rc = pNode->getDataType( pDb, &uiDataType)))
{
goto Exit;
}
ui64NodeId = pNode->getIxNodeId();
uiNameId = pNode->getNameId();
// See if the node is already there
if( !findNodeInList( pNode->getNodeType(),
uiCollection, ui64NodeId,
uiNameId, &pucData, &uiDataLen, &uiInsertPos))
{
// Expand the size of the table.
if (m_uiNodeCount == m_uiListSize)
{
if (RC_BAD( rc = f_realloc( (m_uiListSize + 20) *
sizeof( OLD_NODE_DATA), &m_pNodeList)))
{
goto Exit;
}
m_uiListSize += 20;
}
if (uiInsertPos < m_uiNodeCount)
{
f_memmove( &m_pNodeList [uiInsertPos + 1],
&m_pNodeList [uiInsertPos],
sizeof( OLD_NODE_DATA) * (m_uiNodeCount - uiInsertPos));
}
m_pNodeList [uiInsertPos].eNodeType = pNode->getNodeType();
m_pNodeList [uiInsertPos].uiCollection = uiCollection;
m_pNodeList [uiInsertPos].ui64NodeId = ui64NodeId;
m_pNodeList [uiInsertPos].uiNameId = uiNameId;
m_uiNodeCount++;
// Set up the data - either unicode or binary.
if( uiDataType == XFLM_BINARY_TYPE)
{
// Get the length first.
if( RC_BAD( rc = pNode->getDataLength( pDb, &uiBufSize)))
{
goto Exit;
}
// Allocate the space needed.
if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize,
(void **)&m_pNodeList [uiInsertPos].pucData)))
{
goto Exit;
}
// Go back again and get the data now.
if( RC_BAD( rc = pNode->getBinary( pDb, m_pNodeList [uiInsertPos].pucData,
0, uiBufSize, NULL)))
{
goto Exit;
}
m_pNodeList [uiInsertPos].uiDataLen = uiBufSize;
}
else
{
flmAssert( uiDataType == XFLM_TEXT_TYPE);
// Get the length first.
if( RC_BAD( rc = pNode->getUnicodeChars( pDb, &uiChars)))
{
goto Exit;
}
// Allocate the space needed.
uiBufSize = (uiChars + 1) * sizeof( FLMUNICODE);
if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize,
(void **)&m_pNodeList [uiInsertPos].pucData)))
{
goto Exit;
}
// Go back again and get the data now.
if( RC_BAD( rc = pNode->getUnicode( pDb,
(FLMUNICODE *)m_pNodeList [uiInsertPos].pucData,
uiBufSize, 0, FLM_MAX_UINT)))
{
goto Exit;
}
m_pNodeList [uiInsertPos].uiDataLen = uiBufSize;
}
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Release all of the nodes in the list.
*****************************************************************************/
void F_OldNodeList::resetList( void)
{
m_pool.poolReset( NULL);
m_uiNodeCount = 0;
}
/****************************************************************************
Desc: Add an index key to the buffers
****************************************************************************/
RCODE F_Db::addToKrefTbl(
FLMUINT uiKeyLen,
FLMUINT uiDataLen)
{
RCODE rc = NE_XFLM_OK;
KREF_ENTRY * pKref;
FLMUINT uiSizeNeeded;
FLMBYTE * pucDest;
// If the table is FULL, expand the table
if (m_uiKrefCount == m_uiKrefTblSize)
{
FLMUINT uiAllocSize;
FLMUINT uiOrigKrefTblSize = m_uiKrefTblSize;
if (m_uiKrefTblSize > 0x8000 / sizeof( KREF_ENTRY *))
{
m_uiKrefTblSize += 4096;
}
else
{
m_uiKrefTblSize *= 2;
}
uiAllocSize = m_uiKrefTblSize * sizeof( KREF_ENTRY *);
rc = f_realloc( uiAllocSize, &m_pKrefTbl);
if (RC_BAD(rc))
{
m_uiKrefTblSize = uiOrigKrefTblSize;
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
}
// Allocate memory for the key's KREF and the key itself.
// We allocate one extra byte so we can zero terminate the key
// below. The extra zero character is to ensure that the compare
// in the qsort routine will work.
uiSizeNeeded = sizeof( KREF_ENTRY) + uiKeyLen + 1 + uiDataLen;
if (RC_BAD( rc = m_pKrefPool->poolAlloc( uiSizeNeeded,
(void **)&pKref)))
{
goto Exit;
}
m_pKrefTbl [ m_uiKrefCount++] = pKref;
m_uiTotalKrefBytes += uiSizeNeeded;
// Fill in all of the fields in the KREF structure.
pKref->ui16IxNum = (FLMUINT16)m_keyGenInfo.pIxd->uiIndexNum;
pKref->bDelete = m_keyGenInfo.bAddKeys ? FALSE : TRUE;
pKref->ui16KeyLen = (FLMUINT16)uiKeyLen;
pKref->uiSequence = m_uiKrefCount;
pKref->uiDataLen = uiDataLen;
// Copy the key to just after the KREF structure.
pucDest = (FLMBYTE *)(&pKref [1]);
f_memcpy( pucDest, m_keyGenInfo.pucKeyBuf, uiKeyLen);
// Null terminate the key so compare in qsort will work.
pucDest [uiKeyLen] = 0;
if (uiDataLen)
{
f_memcpy( pucDest + uiKeyLen + 1, m_keyGenInfo.pucData, uiDataLen);
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Verifies that all of the nodes in the CDL list are in contexts that
are related according to the index definition.
****************************************************************************/
RCODE F_Db::verifyKeyContext(
FLMBOOL * pbVerified)
{
RCODE rc = NE_XFLM_OK;
CDL * pCdl;
CDL * pParentCdl;
ICD * pIcd;
*pbVerified = FALSE;
pIcd = m_keyGenInfo.pIxd->pIcdTree;
// Do in-order traversal, from leaf ICDs up.
while (pIcd->pFirstChild)
{
pIcd = pIcd->pFirstChild;
}
for (;;)
{
if ((pCdl = m_keyGenInfo.pCdlTbl [pIcd->uiCdl].pCdlList) != NULL)
{
// If this is a "missing" placeholder and the
// component is required, we cannot build the key
if (!pCdl->pNode && pIcd->uiKeyComponent &&
(pIcd->uiFlags & ICD_REQUIRED_PIECE))
{
goto Exit;
}
// If the ICD has a parent, see if the parent has the
// correct node id.
if (pIcd->pParent)
{
pParentCdl = m_keyGenInfo.pCdlTbl [pIcd->pParent->uiCdl].pCdlList;
if( !pParentCdl || !pParentCdl->pNode)
{
goto Exit;
}
if( pParentCdl->pNode->getNodeId() != pCdl->ui64ParentId)
{
goto Exit;
}
}
}
else
{
if (pIcd->uiKeyComponent && (pIcd->uiFlags & ICD_REQUIRED_PIECE))
{
// This better already have been checked
flmAssert( 0);
goto Exit;
}
}
// See if there is a sibling
if (pIcd->pNextSibling)
{
pIcd = pIcd->pNextSibling;
while (pIcd->pFirstChild)
{
pIcd = pIcd->pFirstChild;
}
}
else
{
if ((pIcd = pIcd->pParent) == NULL)
{
break;
}
}
}
*pbVerified = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc: Build the data part for a key.
Notes: This routine is recursive in nature. Will recurse the number of
data components defined in the index.
****************************************************************************/
RCODE F_Db::buildData(
ICD * pIcd,
FLMUINT uiKeyLen,
FLMUINT uiDataLen
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiCdl;
CDL * pFirstCdl;
CDL * pCdl;
FLMUINT uiDataComponentLen;
F_DOMNode * pNode = NULL;
FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE];
FLMBYTE * pucTmpSen;
FLMUINT uiSENLen;
FLMBOOL bVerified;
FLMUINT uiIDLen;
uiCdl = pIcd->uiCdl;
pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList;
pCdl = pFirstCdl;
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// If we are not using nodes in the sub-tree, skip any
// that are in the sub-tree.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
// Go through all of the data CDL - even the ones that are NULL
for (;;)
{
// Data components cannot be root tags.
flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG);
if (pNode)
{
pNode->Release();
pNode = NULL;
}
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl;
if (pCdl)
{
// NOTE: pNode could be NULL because it is a "missing" placeholder
pNode = pCdl->pNode;
}
if (pNode)
{
pNode->AddRef();
if (RC_BAD( rc = pNode->getDataLength( this, &uiDataComponentLen)))
{
goto Exit;
}
}
else
{
uiDataComponentLen = 0;
}
// Output the length of the data as a SEN value
pucTmpSen = &ucTmpSen [0];
uiSENLen = f_encodeSEN( uiDataComponentLen, &pucTmpSen);
if (uiDataComponentLen + uiSENLen + uiDataLen > m_keyGenInfo.uiDataBufSize)
{
FLMUINT uiNewSize = uiDataComponentLen + uiSENLen + uiDataLen + 512;
// Allocate the data buffer if it has not been allocated. Otherwise,
// realloc it.
if (!m_keyGenInfo.bDataBufAllocated)
{
FLMBYTE * pucNewData;
if (RC_BAD( rc = f_alloc( uiNewSize, &pucNewData)))
{
goto Exit;
}
if( uiDataLen)
{
f_memcpy( pucNewData, m_keyGenInfo.pucData, uiDataLen);
}
m_keyGenInfo.pucData = pucNewData;
m_keyGenInfo.bDataBufAllocated = TRUE;
}
else
{
// Reallocate the buffer.
if (RC_BAD( rc = f_realloc( uiNewSize, &m_keyGenInfo.pucData)))
{
goto Exit;
}
}
m_keyGenInfo.uiDataBufSize = uiNewSize;
}
f_memcpy( m_keyGenInfo.pucData + uiDataLen, ucTmpSen, uiSENLen);
if (uiDataComponentLen)
{
if (RC_BAD( rc = pNode->getData( this,
m_keyGenInfo.pucData + uiDataLen + uiSENLen,
&uiDataComponentLen)))
{
goto Exit;
}
}
// If this is the last data CDL, append IDs to the
// key and output the key and data to the KREF.
// Otherwise, recurse down.
if (pIcd->pNextDataComponent)
{
if (RC_BAD( rc = buildData( pIcd->pNextDataComponent, uiKeyLen,
uiDataLen + uiDataComponentLen + uiSENLen)))
{
goto Exit;
}
}
else if (m_keyGenInfo.pIxd->pFirstContext)
{
if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen,
uiDataLen + uiDataComponentLen + uiSENLen)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = verifyKeyContext( &bVerified)))
{
goto Exit;
}
if (bVerified)
{
if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID,
m_keyGenInfo.pIxd,
m_keyGenInfo.pCdlTbl,
&m_keyGenInfo.pucKeyBuf [uiKeyLen],
XFLM_MAX_KEY_SIZE - uiKeyLen, &uiIDLen)))
{
goto Exit;
}
if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen,
uiDataLen + uiDataComponentLen + uiSENLen)))
{
goto Exit;
}
}
}
// Get the next CDL, if any
if (pCdl)
{
pCdl = pCdl->pNext;
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// If we are not using nodes in the sub-tree, skip any
// that are in the sub-tree.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
}
if (!pCdl)
{
goto Exit;
}
}
Exit:
if (pNode)
{
pNode->Release();
}
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl;
return( rc);
}
/****************************************************************************
Desc: Go through the context components of a key.
Notes: This routine is recursive in nature. Will recurse the number of
context components defined in the index.
****************************************************************************/
RCODE F_Db::buildContext(
ICD * pIcd,
FLMUINT uiKeyLen,
FLMUINT uiDataLen
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiCdl;
CDL * pFirstCdl;
CDL * pCdl;
FLMUINT uiIDLen;
F_DOMNode * pNode = NULL;
FLMBOOL bVerified;
uiCdl = pIcd->uiCdl;
pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList;
pCdl = pFirstCdl;
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// If we are not using nodes in the sub-tree, skip any
// that are in the sub-tree.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
// Go through all of the context CDLs - even the ones that are NULL
for (;;)
{
if (pNode)
{
pNode->Release();
pNode = NULL;
}
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl;
if (pCdl)
{
// NOTE: pNode could be NULL because it is a "missing" placeholder
if ((pNode = pCdl->pNode) != NULL)
{
pNode->AddRef();
}
}
// If this is the last context CDL, append IDs to the
// key and output the key and data to the KREF.
// Otherwise, recurse down.
if (pIcd->pNextKeyComponent)
{
if (RC_BAD( rc = buildContext( pIcd->pNextKeyComponent,
uiKeyLen, uiDataLen)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = verifyKeyContext( &bVerified)))
{
goto Exit;
}
if (bVerified)
{
if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID,
m_keyGenInfo.pIxd,
m_keyGenInfo.pCdlTbl,
&m_keyGenInfo.pucKeyBuf [uiKeyLen],
XFLM_MAX_KEY_SIZE - uiKeyLen, &uiIDLen)))
{
goto Exit;
}
if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, uiDataLen)))
{
goto Exit;
}
}
}
// Get the next CDL, if any
if (pCdl)
{
pCdl = pCdl->pNext;
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// If we are not using nodes in the sub-tree, skip any
// that are in the sub-tree.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
}
if (!pCdl)
{
goto Exit;
}
}
Exit:
if (pNode)
{
pNode->Release();
}
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl;
return( rc);
}
/****************************************************************************
Desc: Finish the current key component. If there is a next one call
build keys. Otherwise, go on to doing data and context pieces.
****************************************************************************/
RCODE F_Db::finishKeyComponent(
ICD * pIcd,
FLMUINT uiKeyLen
)
{
RCODE rc = NE_XFLM_OK;
if (pIcd->pNextKeyComponent)
{
flmAssert( m_keyGenInfo.bIsCompound);
if (RC_BAD( rc = buildKeys( pIcd->pNextKeyComponent, uiKeyLen)))
{
goto Exit;
}
}
else
{
if (m_keyGenInfo.pIxd->pFirstData)
{
if (RC_BAD( rc = buildData( m_keyGenInfo.pIxd->pFirstData, uiKeyLen, 0)))
{
goto Exit;
}
}
else if (m_keyGenInfo.pIxd->pFirstContext)
{
if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen, 0)))
{
goto Exit;
}
}
else
{
FLMBOOL bVerified;
if (RC_BAD( rc = verifyKeyContext( &bVerified)))
{
goto Exit;
}
if (bVerified)
{
FLMUINT uiIDLen;
if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID,
m_keyGenInfo.pIxd,
m_keyGenInfo.pCdlTbl,
&m_keyGenInfo.pucKeyBuf [uiKeyLen],
XFLM_MAX_KEY_SIZE - uiKeyLen,
&uiIDLen)))
{
goto Exit;
}
if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, 0)))
{
goto Exit;
}
}
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Generate the keys for a text component.
****************************************************************************/
RCODE F_Db::genTextKeyComponents(
F_DOMNode * pNode,
ICD * pIcd,
FLMUINT uiKeyLen,
FLMBYTE ** ppucTmpBuf,
FLMUINT * puiTmpBufSize,
void ** ppvMark)
{
RCODE rc = NE_XFLM_OK;
IF_PosIStream * pIStream = NULL;
FLMUINT uiNumChars;
FLMUINT uiStrBytes;
FLMUINT uiSubstrChars;
FLMUINT uiMeta;
FLMBOOL bEachWord = FALSE;
FLMBOOL bMetaphone = FALSE;
FLMBOOL bSubstring = FALSE;
FLMBOOL bWholeString = FALSE;
FLMBOOL bHadAtLeastOneString = FALSE;
FLMBOOL bDataTruncated;
FLMUINT uiSaveKeyLen;
FLMUINT uiElmLen;
FLMUINT uiKeyLenPos = uiKeyLen;
FLMUINT uiCompareRules = pIcd->uiCompareRules;
F_NodeBufferIStream nodeBufferIStream;
IF_BufferIStream * pBufferIStream = NULL;
uiKeyLen += 2;
uiSaveKeyLen = uiKeyLen;
if (!pNode)
{
goto No_Strings;
}
if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream)))
{
goto Exit;
}
if (RC_BAD( rc = pNode->getTextIStream( this,
&nodeBufferIStream, &pIStream, &uiNumChars)))
{
goto Exit;
}
if (!uiNumChars)
{
No_Strings:
// Save the key component length
UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
if( pIStream)
{
pIStream->Release();
pIStream = NULL;
}
rc = finishKeyComponent( pIcd, uiKeyLen);
goto Exit;
}
if (pIcd->uiFlags & ICD_EACHWORD)
{
bEachWord = TRUE;
// OR in the compressing of spaces, because we only want to treat
// spaces as delimiters between words.
uiCompareRules |= XFLM_COMP_COMPRESS_WHITESPACE;
}
else if (pIcd->uiFlags & ICD_METAPHONE)
{
bMetaphone = TRUE;
}
else if (pIcd->uiFlags & ICD_SUBSTRING)
{
bSubstring = TRUE;
}
else
{
bWholeString = TRUE;
}
// Loop on each word or substring in the value
for (;;)
{
uiKeyLen = uiSaveKeyLen;
bDataTruncated = FALSE;
if (bWholeString)
{
uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen;
if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen],
&uiElmLen,
pIStream, XFLM_TEXT_TYPE,
pIcd->uiFlags, pIcd->uiCompareRules,
pIcd->uiLimit, NULL, NULL,
m_keyGenInfo.pIxd->uiLanguage,
FALSE, FALSE,
&bDataTruncated, NULL)))
{
goto Exit;
}
}
else if (bEachWord)
{
if (*ppucTmpBuf == NULL)
{
*ppvMark = m_tempPool.poolMark();
*puiTmpBufSize = (FLMUINT)XFLM_MAX_KEY_SIZE + 8;
if (RC_BAD( rc = m_tempPool.poolAlloc( *puiTmpBufSize,
(void **)ppucTmpBuf)))
{
goto Exit;
}
}
uiStrBytes = *puiTmpBufSize;
if( RC_BAD( rc = KYEachWordParse( pIStream, &uiCompareRules,
pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes)))
{
goto Exit;
}
if (!uiStrBytes)
{
if (!bHadAtLeastOneString)
{
goto No_Strings;
}
break;
}
if (RC_BAD( rc = pBufferIStream->openStream(
(const char *)*ppucTmpBuf, uiStrBytes)))
{
goto Exit;
}
// Pass 0 for compare rules because KYEachWordParse will already
// have taken care of them - except for XFLM_COMP_CASE_INSENSITIVE.
uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen;
rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen],
&uiElmLen,
pBufferIStream, XFLM_TEXT_TYPE,
pIcd->uiFlags,
pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE,
pIcd->uiLimit,
NULL, NULL,
m_keyGenInfo.pIxd->uiLanguage,
FALSE, FALSE, &bDataTruncated, NULL);
pBufferIStream->closeStream();
if( RC_BAD( rc))
{
RC_UNEXPECTED_ASSERT( rc);
goto Exit;
}
bHadAtLeastOneString = TRUE;
}
else if (bMetaphone)
{
FLMBYTE ucStorageBuf[ FLM_MAX_NUM_BUF_SIZE];
FLMUINT uiStorageLen;
if( RC_BAD( rc = f_getNextMetaphone( pIStream, &uiMeta)))
{
if( rc != NE_XFLM_EOF_HIT)
{
goto Exit;
}
rc = NE_XFLM_OK;
if (!bHadAtLeastOneString)
{
goto No_Strings;
}
break;
}
uiStorageLen = FLM_MAX_NUM_BUF_SIZE;
if( RC_BAD( rc = flmNumber64ToStorage( uiMeta,
&uiStorageLen, ucStorageBuf, FALSE, FALSE)))
{
goto Exit;
}
if (RC_BAD( rc = pBufferIStream->openStream(
(const char *)ucStorageBuf, uiStorageLen)))
{
goto Exit;
}
// Pass 0 for compare rules - only applies to strings.
uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen;
rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen],
&uiElmLen,
pBufferIStream, XFLM_NUMBER_TYPE,
pIcd->uiFlags, 0,
pIcd->uiLimit,
NULL, NULL,
m_keyGenInfo.pIxd->uiLanguage,
FALSE, FALSE, NULL, NULL);
pBufferIStream->closeStream();
if( RC_BAD( rc))
{
RC_UNEXPECTED_ASSERT( rc);
goto Exit;
}
bHadAtLeastOneString = TRUE;
}
else
{
flmAssert( bSubstring);
if (*ppucTmpBuf == NULL)
{
*ppvMark = m_tempPool.poolMark();
*puiTmpBufSize = (FLMUINT)XFLM_MAX_KEY_SIZE + 8;
if (RC_BAD( rc = m_tempPool.poolAlloc( *puiTmpBufSize,
(void **)ppucTmpBuf)))
{
goto Exit;
}
}
uiStrBytes = *puiTmpBufSize;
if( RC_BAD( rc = KYSubstringParse( pIStream, &uiCompareRules,
pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes, &uiSubstrChars)))
{
goto Exit;
}
if (!uiStrBytes)
{
if (!bHadAtLeastOneString)
{
goto No_Strings;
}
break;
}
if (bHadAtLeastOneString && uiSubstrChars == 1 && !m_keyGenInfo.bIsAsia)
{
break;
}
if (RC_BAD( rc = pBufferIStream->openStream(
(const char *)*ppucTmpBuf, uiStrBytes)))
{
goto Exit;
}
// Pass 0 for compare rules, because KYSubstringParse has already
// taken care of them, except for XFLM_COMP_CASE_INSENSITIVE
uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen;
rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen],
&uiElmLen,
pBufferIStream, XFLM_TEXT_TYPE,
pIcd->uiFlags,
pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE,
pIcd->uiLimit,
NULL, NULL,
m_keyGenInfo.pIxd->uiLanguage,
bHadAtLeastOneString ? FALSE : TRUE, FALSE,
&bDataTruncated, NULL);
pBufferIStream->closeStream();
if( RC_BAD( rc))
{
RC_UNEXPECTED_ASSERT( rc);
goto Exit;
}
bHadAtLeastOneString = TRUE;
}
uiKeyLen += uiElmLen;
// Save the key component length
if (!bDataTruncated)
{
UW2FBA( (FLMUINT16)uiElmLen, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
}
else
{
UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG),
&m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
// If we are deleting, save the node into the list of "old" nodes
// We do this so that the compare routine can look for the node
// if it has to compare the full value.
if (!m_keyGenInfo.bAddKeys)
{
if (!m_pOldNodeList)
{
if ((m_pOldNodeList = f_new F_OldNodeList) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
}
if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode)))
{
goto Exit;
}
}
}
if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen)))
{
goto Exit;
}
if (bWholeString)
{
break;
}
}
Exit:
if( pBufferIStream)
{
pBufferIStream->Release();
}
if (pIStream)
{
pIStream->Release();
pIStream = NULL;
}
return( rc);
}
/****************************************************************************
Desc: Generate the keys for other data types besides text.
****************************************************************************/
RCODE F_Db::genOtherKeyComponent(
F_DOMNode * pNode,
ICD * pIcd,
FLMUINT uiKeyLen)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiElmLen;
FLMUINT uiKeyLenPos = uiKeyLen;
FLMBOOL bDataTruncated;
IF_PosIStream * pIStream = NULL;
F_NodeBufferIStream bufferIStream;
uiKeyLen += 2;
if (!pNode)
{
No_Data:
// Save the key component length
UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
rc = finishKeyComponent( pIcd, uiKeyLen);
goto Exit;
}
if (pIcd->uiFlags & ICD_PRESENCE)
{
FLMUINT uiNameId = pIcd->uiDictNum;
// If we are indexing ELM_ROOT_TAG, we
// need to get the name id from the node.
if (uiNameId == ELM_ROOT_TAG)
{
if (RC_BAD( rc = pNode->getNameId( this, &uiNameId)))
{
goto Exit;
}
}
f_UINT32ToBigEndian( (FLMUINT32)uiNameId,
&m_keyGenInfo.pucKeyBuf [uiKeyLen]);
uiKeyLen += 4;
// Save the key component length.
UW2FBA( 4, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
}
else
{
// If it is not a presence index, we cannot be indexing
// ELM_ROOT_TAG on an element node.
flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG);
if (RC_BAD( rc = pNode->getIStream( this, &bufferIStream, &pIStream)))
{
goto Exit;
}
if (!pIStream->remainingSize())
{
goto No_Data;
}
// Compute number of bytes left
uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen;
bDataTruncated = FALSE;
// Pass zero for compare rules - these are not strings.
if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen],
&uiElmLen, pIStream, icdGetDataType( pIcd), pIcd->uiFlags,
0, pIcd->uiLimit, NULL, NULL,
m_keyGenInfo.pIxd->uiLanguage,
FALSE, FALSE,
&bDataTruncated, NULL)))
{
goto Exit;
}
uiKeyLen += uiElmLen;
// Save the key component length.
if (!bDataTruncated)
{
UW2FBA( (FLMUINT16)uiElmLen,
&m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
}
else
{
UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG),
&m_keyGenInfo.pucKeyBuf [uiKeyLenPos]);
// If we are deleting, save the node into the list of "old" nodes
// We do this so that the compare routine can look for the node
// if it has to compare the full value.
if (!m_keyGenInfo.bAddKeys)
{
if (!m_pOldNodeList)
{
if ((m_pOldNodeList = f_new F_OldNodeList) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
}
if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode)))
{
goto Exit;
}
}
}
// Better have an F_IStream at this point!
flmAssert( pIStream);
pIStream->Release();
pIStream = NULL;
}
if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen)))
{
goto Exit;
}
Exit:
if (pIStream)
{
pIStream->Release();
}
return( rc);
}
/****************************************************************************
Desc: Build all compound keys from the CDL table.
Notes: This routine is recursive in nature. Will recurse the number of
key components defined in the index.
****************************************************************************/
RCODE F_Db::buildKeys(
ICD * pIcd,
FLMUINT uiKeyLen
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiCdl = pIcd->uiCdl;
CDL * pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList;
CDL * pCdl = pFirstCdl;
FLMBYTE * pucTmpBuf = NULL;
void * pvMark = NULL;
FLMUINT uiTmpBufSize = 0;
F_DOMNode * pNode = NULL;
flmAssert( m_keyGenInfo.bIsCompound || pIcd->uiKeyComponent == 1);
// Do each CDL. If there is no CDL for this level, must
// still do at least once so that if there are other
// components or data after this component, we will
// do a recursive call or generate a key.
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// Skip any CDLs in the subtree if we are not using
// sub-tree nodes at this point.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
for (;;)
{
// Need to set the current CDL into the table so that
// when we append the IDs we will have the right ones.
// This will be restored to the first item in the
// table at Exit.
if (pNode)
{
pNode->Release();
pNode = NULL;
}
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl;
if (pCdl)
{
// pNode could be NULL because it is a placeholder for a
// "missing" value
if ((pNode = pCdl->pNode) != NULL)
{
pNode->AddRef();
}
}
// Generate the key component
if (icdGetDataType( pIcd) == XFLM_TEXT_TYPE &&
!(pIcd->uiFlags & ICD_PRESENCE))
{
if (RC_BAD( rc = genTextKeyComponents( pNode, pIcd, uiKeyLen,
&pucTmpBuf, &uiTmpBufSize, &pvMark)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = genOtherKeyComponent( pNode, pIcd, uiKeyLen)))
{
goto Exit;
}
}
// Go to the next CDL, if any
if (pCdl)
{
pCdl = pCdl->pNext;
if (!m_keyGenInfo.bUseSubtreeNodes)
{
// Skip any CDLs in the subtree if we are not using
// sub-tree nodes at this point.
while (pCdl && pCdl->bInNodeSubtree)
{
pCdl = pCdl->pNext;
}
}
}
if (!pCdl)
{
break;
}
}
Exit:
if (pNode)
{
pNode->Release();
}
if (pvMark)
{
m_tempPool.poolReset( pvMark);
}
// Restore the CDL table entry to point to the
// beginning of the list
m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl;
return( rc);
}
/****************************************************************************
Desc: Build all keys from combinations of CDLs. Add keys to KREF table.
****************************************************************************/
RCODE F_Db::buildKeys(
FLMUINT64 ui64DocumentID,
IXD * pIxd,
CDL_HDR * pCdlTbl,
FLMBOOL bUseSubtreeNodes,
FLMBOOL bAddKeys)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiNumKeysHavingCdl;
CDL * pCdl;
ICD * pIcd;
FLMBYTE ucDataBuf [STACK_DATA_BUF_SIZE];
m_keyGenInfo.bDataBufAllocated = FALSE;
// Do a quick check to make sure we have all required pieces
uiNumKeysHavingCdl = 0;
pIcd = pIxd->pFirstKey;
while (pIcd)
{
pCdl = pCdlTbl [pIcd->uiCdl].pCdlList;
if (!bUseSubtreeNodes)
{
// Skip any nodes in the subtree if we are not using
// sub-tree nodes at this point. Also skip any
// "missing" placeholders.
while (pCdl && (pCdl->bInNodeSubtree || !pCdl->pNode))
{
pCdl = pCdl->pNext;
}
}
else
{
// Skip any "missing" placeholders.
while (pCdl && !pCdl->pNode)
{
pCdl = pCdl->pNext;
}
}
if (!pCdl)
{
if (pIcd->uiFlags & ICD_REQUIRED_PIECE)
{
goto Exit; // Nothing to generate, a required piece is missing.
}
}
else
{
uiNumKeysHavingCdl++;
}
pIcd = pIcd->pNextKeyComponent;
}
// If none of the key pieces had a CDL, we cannot generate a key either.
if (!uiNumKeysHavingCdl)
{
goto Exit;
}
// Build all of the keys
m_keyGenInfo.ui64DocumentID = ui64DocumentID;
m_keyGenInfo.pIxd = pIxd;
m_keyGenInfo.bIsAsia = (FLMBOOL)(pIxd->uiLanguage >= FLM_FIRST_DBCS_LANG &&
pIxd->uiLanguage <= FLM_LAST_DBCS_LANG)
? TRUE
: FALSE;
m_keyGenInfo.bIsCompound = pIxd->uiNumKeyComponents > 1 ? TRUE : FALSE;
m_keyGenInfo.pCdlTbl = pCdlTbl;
m_keyGenInfo.bUseSubtreeNodes = bUseSubtreeNodes;
m_keyGenInfo.bAddKeys = bAddKeys;
m_keyGenInfo.pucKeyBuf = m_pucKrefKeyBuf;
m_keyGenInfo.pucData = &ucDataBuf [0];
m_keyGenInfo.uiDataBufSize = sizeof( ucDataBuf);
m_keyGenInfo.bDataBufAllocated = FALSE;
if (RC_BAD( rc = buildKeys( pIxd->pFirstKey, 0)))
{
goto Exit;
}
Exit:
if (m_keyGenInfo.bDataBufAllocated)
{
f_free( &m_keyGenInfo.pucData);
m_keyGenInfo.bDataBufAllocated = FALSE;
}
return( rc);
}
/****************************************************************************
Desc: See if the passed in node has any other siblings with the same ID.
The passed in node must be an element node.
****************************************************************************/
FSTATIC RCODE kySeeIfRepeatingSibs(
F_Db * pDb,
F_DOMNode * pNode,
FLMBOOL * pbHadRepeatingSib)
{
RCODE rc = NE_XFLM_OK;
F_DOMNode * pTmpNode = NULL;
FLMUINT uiNameId;
FLMUINT uiTmpNameId;
flmAssert( pNode->getNodeType() == ELEMENT_NODE);
if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId)))
{
goto Exit;
}
// Traverse next siblings
pTmpNode = pNode;
pTmpNode->AddRef();
for (;;)
{
if( RC_BAD( rc = pTmpNode->getNextSibling( pDb,
(IF_DOMNode **)&pTmpNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
break;
}
if( pTmpNode->getNodeType() == ELEMENT_NODE)
{
if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId)))
{
goto Exit;
}
if (uiTmpNameId == uiNameId)
{
*pbHadRepeatingSib = TRUE;
goto Exit;
}
}
}
// Traverse previous siblings
pTmpNode->Release();
pTmpNode = pNode;
pTmpNode->AddRef();
for (;;)
{
if( RC_BAD( rc = pTmpNode->getPreviousSibling( pDb,
(IF_DOMNode **)&pTmpNode)))
{
if( rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
break;
}
if (pTmpNode->getNodeType() == ELEMENT_NODE)
{
if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId)))
{
goto Exit;
}
if (uiTmpNameId == uiNameId)
{
*pbHadRepeatingSib = TRUE;
goto Exit;
}
}
}
Exit:
if (pTmpNode)
{
pTmpNode->Release();
}
return( rc);
}
/****************************************************************************
Desc: Get a child node for current traversal node.
****************************************************************************/
FSTATIC RCODE kyFindChildNode(
F_Db * pDb,
F_Pool * pPool,
NODE_TRAV ** ppTrav,
FLMBOOL * pbGotChild,
FLMBOOL * pbHadRepeatingSib)
{
RCODE rc = NE_XFLM_OK;
F_DOMNode * pNode = NULL;
ICD * pIcd;
FLMUINT uiNameId;
NODE_TRAV * pTrav = *ppTrav;
FLMUINT uiElmIcdCount = 0;
FLMUINT uiAttrIcdCount = 0;
FLMBOOL bTraverseElms;
FLMBOOL bTraverseAttrs;
ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode;
FLMUINT uiAttrNameId = 0;
if( pAnchorNode &&
pTrav->pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId)
{
pAnchorNode = pAnchorNode->pNext;
}
else
{
pAnchorNode = NULL;
}
*pbGotChild = FALSE;
// Determine if we need to traverse elements, attributes,
// or both.
pIcd = pTrav->pIcd->pFirstChild;
while (pIcd)
{
if (pIcd->uiFlags & ICD_IS_ATTRIBUTE)
{
uiAttrNameId = pIcd->uiDictNum;
uiAttrIcdCount++;
}
else
{
uiElmIcdCount++;
}
pIcd = pIcd->pNextSibling;
}
bTraverseElms = (uiElmIcdCount) ? TRUE : FALSE;
bTraverseAttrs = (uiAttrIcdCount) ? TRUE : FALSE;
flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE);
if (bTraverseElms)
{
bTraverseElms = FALSE;
if (RC_BAD( rc = pTrav->pNode->getFirstChild( pDb,
(IF_DOMNode **)&pNode)) && rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
if (bTraverseAttrs)
{
bTraverseAttrs = FALSE;
rc = pTrav->pNode->getFirstAttribute( pDb,
(IF_DOMNode **)&pNode);
}
else
{
rc = NE_XFLM_OK;
goto Exit;
}
}
}
else
{
flmAssert( bTraverseAttrs);
bTraverseAttrs = FALSE;
// If we only have a single attribute we are searching for,
// it will be quicker to call getAttribute than to cycle through
// all of the attributes.
if (uiAttrIcdCount == 1)
{
rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId,
(IF_DOMNode **)&pNode);
}
else
{
rc = pTrav->pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode);
}
}
if (RC_BAD( rc))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
// Follow siblings until we have a match to any of the sibling ICDs
// Attribute nodes are also considered siblings.
for (;;)
{
eDomNodeType eNodeType = pNode->getNodeType();
// Skip any nodes that are not element or attribute nodes.
if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE)
{
goto Next_Node;
}
if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId)))
{
goto Exit;
}
// If the node has the same name id as the anchor node,
// we need to skip it, unless it is the anchor node itself
if( pAnchorNode &&
uiNameId == pAnchorNode->uiAnchorNameId &&
pNode->getNodeType() == pAnchorNode->eNodeType &&
pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId)
{
// Better not be repeated attribute nodes
flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE);
if (pAnchorNode->bSeeIfRepeatingSibs)
{
*pbHadRepeatingSib = TRUE;
}
pIcd = NULL;
}
else
{
pIcd = pTrav->pIcd->pFirstChild;
if (pNode->getNodeType() == ELEMENT_NODE)
{
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pNextSibling;
}
}
else // pNode->getNodeType() == ATTRIBUTE_NODE
{
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
!(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pNextSibling;
}
}
}
if (pIcd)
{
if (!pTrav->pChild)
{
NODE_TRAV * pNewTrav;
if (RC_BAD( rc = pPool->poolCalloc( sizeof( NODE_TRAV),
(void **)&pNewTrav)))
{
goto Exit;
}
pNewTrav->pParent = pTrav;
pTrav->pChild = pNewTrav;
}
else
{
f_memset( pTrav->pChild, 0, sizeof( NODE_TRAV));
pTrav->pChild->pParent = pTrav;
}
*ppTrav = pTrav = pTrav->pChild;
pTrav->pNode = pNode;
pTrav->pNode->AddRef();
pTrav->pAnchorNode = pAnchorNode;
pTrav->uiSibIcdElms = uiElmIcdCount;
pTrav->uiSibIcdAttrs = uiAttrIcdCount;
pTrav->pIcd = pIcd;
if (pNode->getNodeType() == ATTRIBUTE_NODE)
{
// There will only be one occurrence of any given
// attribute node, so once we have found it, there
// is no need to traverse any other siblings if
// there are no other sibling ICDs that are
// attributes. At this point, the number of ICDs
// that are elements is irrelevant, because we would
// have already traversed through all of them.
pTrav->bTraverseSibs = uiAttrIcdCount > 1 ? TRUE : FALSE;
// Attributes don't have children
pTrav->bTraverseChildren = FALSE;
flmAssert( !pIcd->pFirstChild);
}
else
{
FLMBOOL bIsAnchor = FALSE;
if( pAnchorNode)
{
if( pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId)
{
bIsAnchor = TRUE;
}
}
// If there are attribute ICDs or more than one
// element ICD, or there is exactly one element
// ICD, but this is the anchor node, then no
// need to traverse siblings.
if (uiAttrIcdCount || uiElmIcdCount > 1 || !bIsAnchor)
{
pTrav->bTraverseSibs = TRUE;
}
else
{
pTrav->bTraverseSibs = FALSE;
// No need to specially traverse siblings searching
// for repeating siblings if bTraverseSibs is set
// to TRUE.
if (bIsAnchor && pAnchorNode->bSeeIfRepeatingSibs &&
!(*pbHadRepeatingSib))
{
if (RC_BAD( rc = kySeeIfRepeatingSibs( pDb, pNode,
pbHadRepeatingSib)))
{
goto Exit;
}
}
}
// If we have a child ICD, we need to traverse child nodes.
pTrav->bTraverseChildren = pIcd->pFirstChild
? TRUE
: FALSE;
}
*pbGotChild = TRUE;
goto Exit; // Will return NE_XFLM_OK
}
Next_Node:
// Try the next sibling node
if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
// If we have not yet traversed attributes, do it now.
if (!bTraverseAttrs)
{
break;
}
bTraverseAttrs = FALSE;
// If there is only one attribute, it is quicker to call
// getAttribute than it is to cycle through the attributes.
if (uiAttrIcdCount == 1)
{
rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId,
(IF_DOMNode **)&pNode);
}
else
{
rc = pTrav->pNode->getFirstAttribute( pDb,
(IF_DOMNode **)&pNode);
}
if (RC_BAD( rc))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
}
}
Exit:
if (!(*pbGotChild))
{
pTrav->bTraverseChildren = FALSE;
}
if (pNode)
{
pNode->Release();
}
return( rc);
}
/****************************************************************************
Desc: Get a sibling node for current traversal node.
****************************************************************************/
FSTATIC RCODE kyFindSibNode(
F_Db * pDb,
NODE_TRAV * pTrav,
FLMBOOL bTestFirstNode,
FLMBOOL * pbGotSib,
FLMBOOL * pbHadRepeatingSib
)
{
RCODE rc = NE_XFLM_OK;
F_DOMNode * pNode = NULL;
ICD * pIcd;
FLMUINT uiNameId;
FLMBOOL bTraverseAttrs = pTrav->uiSibIcdAttrs ? TRUE : FALSE;
FLMBOOL bTraverseElms = pTrav->uiSibIcdElms ? TRUE : FALSE;
FLMBOOL bGetNextNode;
ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode;
eDomNodeType eNodeType;
*pbGotSib = FALSE;
pNode = pTrav->pNode;
pNode->AddRef();
eNodeType = pNode->getNodeType();
if( eNodeType == ELEMENT_NODE)
{
flmAssert( bTraverseElms);
}
else
{
flmAssert( eNodeType != DATA_NODE);
flmAssert( bTraverseAttrs);
bTraverseAttrs = FALSE;
}
bTraverseElms = FALSE;
// Follow siblings until we have a match to any of the sibling ICDs
// Attribute nodes are also considered siblings.
bGetNextNode = bTestFirstNode ? FALSE : TRUE;
for (;;)
{
if (bGetNextNode)
{
if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
// If we have not yet traversed attributes, do it now.
if (!bTraverseAttrs)
{
break;
}
bTraverseAttrs = FALSE;
if (RC_OK( rc = pTrav->pNode->getParentNode( pDb,
(IF_DOMNode **)&pNode)))
{
rc = pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode);
}
if (RC_BAD( rc))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
}
}
else
{
// Set to TRUE for next time around.
bGetNextNode = TRUE;
}
// Skip any nodes that are not element or attribute nodes
eNodeType = pNode->getNodeType();
if( eNodeType != ELEMENT_NODE &&
eNodeType != ATTRIBUTE_NODE)
{
continue;
}
if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId)))
{
goto Exit;
}
// If the node has the same name id as the anchor node,
// we need to skip it, unless it is the anchor node itself
if (pAnchorNode &&
eNodeType == pAnchorNode->eNodeType &&
uiNameId == pAnchorNode->uiAnchorNameId &&
pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId)
{
// Better not be repeated attribute nodes
flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE);
if (pAnchorNode->bSeeIfRepeatingSibs)
{
*pbHadRepeatingSib = TRUE;
}
pIcd = NULL;
}
else if( eNodeType == ELEMENT_NODE)
{
// Search forward for a matching ICD.
pIcd = pTrav->pIcd;
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pNextSibling;
}
// If didn't find a matching ICD in the forward direction,
// search backward.
if (!pIcd)
{
pIcd = pTrav->pIcd->pPrevSibling;
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pPrevSibling;
}
}
}
else
{
flmAssert( eNodeType == ATTRIBUTE_NODE);
// Search forward for a matching ICD.
pIcd = pTrav->pIcd;
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
!(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pNextSibling;
}
// If didn't find a matching ICD in the forward direction,
// search backward.
if (!pIcd)
{
pIcd = pTrav->pIcd->pPrevSibling;
while (pIcd &&
(pIcd->uiDictNum != uiNameId ||
!(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
{
pIcd = pIcd->pPrevSibling;
}
}
}
if (pIcd)
{
pTrav->pNode->Release();
pTrav->pNode = pNode;
pTrav->pNode->AddRef();
pTrav->pIcd = pIcd;
if( eNodeType == ATTRIBUTE_NODE)
{
// Attributes don't have children
pTrav->bTraverseChildren = FALSE;
flmAssert( !pIcd->pFirstChild);
}
else
{
// If we have a child ICD, we need to traverse child nodes.
pTrav->bTraverseChildren = pIcd->pFirstChild
? TRUE
: FALSE;
}
*pbGotSib = TRUE;
goto Exit;
}
}
Exit:
if (!(*pbGotSib))
{
pTrav->bTraverseSibs = FALSE;
}
if (pNode)
{
pNode->Release();
}
return( rc);
}
/****************************************************************************
Desc: Release all nodes a CDL table is pointing to.
****************************************************************************/
void kyReleaseCdls(
IXD * pIxd,
CDL_HDR * pCdlTbl
)
{
FLMUINT uiLoop;
CDL * pCdl;
if (pCdlTbl)
{
for (uiLoop = 0; uiLoop < pIxd->uiNumIcds; uiLoop++)
{
pCdl = pCdlTbl [uiLoop].pCdlList;
while (pCdl)
{
// NOTE: pNode can be NULL because it may be a
// placeholder for a "missing" component
if (pCdl->pNode)
{
pCdl->pNode->Release();
}
pCdl = pCdl->pNext;
}
pCdlTbl [uiLoop].pCdlList = NULL;
}
}
}
/****************************************************************************
Desc: Generate index keys from a given node for a particular
index.
****************************************************************************/
RCODE F_Db::genIndexKeys(
FLMUINT64 ui64DocumentID,
F_DOMNode * pIxNode,
IXD * pIxd,
ICD * pIcd,
IxAction eAction)
{
RCODE rc = NE_XFLM_OK;
ICD * pTmpIcd;
ICD * pChildIcd;
CDL_HDR * pCdlTbl = NULL;
CDL * pCdl;
ANCHOR_NODE * pAnchorNodeList;
ANCHOR_NODE * pAnchorNode;
NODE_TRAV * pTrav = NULL;
FLMBOOL bGotNode;
F_DOMNode * pParent = NULL;
F_DOMNode * pTmpNode = NULL;
FLMUINT uiParentNameId;
FLMUINT uiIxNodeName;
FLMBOOL bInNodeSubtree;
FLMBOOL bHadRepeatingSib = FALSE;
eDomNodeType eIxNodeType = pIxNode->getNodeType();
FLMUINT64 ui64IxNodeId = pIxNode->getIxNodeId();
pAnchorNodeList = NULL;
if( RC_BAD( rc = pIxNode->getNameId( this, &uiIxNodeName)))
{
goto Exit;
}
// Follow links back up to highest parent. If the path doesn't
// match what we have in the index definition, then this node
// is irrelevant to generating keys in this index.
if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( ANCHOR_NODE),
(void **)&pAnchorNode)))
{
goto Exit;
}
pAnchorNode->eNodeType = eIxNodeType;
pAnchorNode->ui64AnchorNodeId = ui64IxNodeId;
pAnchorNode->uiAnchorNameId = uiIxNodeName;
// If the action is link/unlink, and the node is an element node
// because it is possible for elements to be repeated, we need
// to see if there is another sibling node of this same name.
// If there is another sibling with this same name, we do not
// need to add the "without" keys back in on the unlink action.
// Nor do we need to delete the "without" keys on the link
// action.
if ((eAction == IX_UNLINK_NODE || eAction == IX_LINK_NODE) &&
eIxNodeType == ELEMENT_NODE)
{
pAnchorNode->bSeeIfRepeatingSibs = TRUE;
}
pAnchorNodeList = pAnchorNode;
pTmpIcd = pIcd;
pParent = pIxNode;
pParent->AddRef();
while (pTmpIcd->pParent)
{
pTmpIcd = pTmpIcd->pParent;
if (RC_BAD( rc = pParent->getParentNode( this, (IF_DOMNode **)&pParent)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
if (RC_BAD( rc = pParent->getNameId( this, &uiParentNameId)))
{
goto Exit;
}
if (pTmpIcd->uiDictNum == ELM_ROOT_TAG)
{
// If this node has a parent, it is not the root node, so
// we don't match the ICD.
if (pParent->getParentId())
{
goto Exit; // Will return NE_XFLM_OK
}
// Better not be any more parent ICDs.
flmAssert( !pTmpIcd->pParent);
}
else
{
if (pTmpIcd->uiDictNum != uiParentNameId)
{
goto Exit; // Will return NE_XFLM_OK
}
}
if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( ANCHOR_NODE),
(void **)&pAnchorNode)))
{
goto Exit;
}
pAnchorNode->eNodeType = pParent->getNodeType();
flmAssert( pAnchorNode->eNodeType == ELEMENT_NODE);
pAnchorNode->ui64AnchorNodeId = pParent->getNodeId();
pAnchorNode->uiAnchorNameId = uiParentNameId;
pAnchorNode->pNext = pAnchorNodeList;
pAnchorNodeList = pAnchorNode;
}
// Allocate a CDL table for the index.
if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( CDL_HDR) *
pIxd->uiNumIcds, (void **)&pCdlTbl)))
{
goto Exit;
}
// Create a traversal node for the root node we arrived at.
if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( NODE_TRAV),
(void **)&pTrav)))
{
goto Exit;
}
pTrav->pNode = pParent;
pTrav->pNode->AddRef();
pTrav->pIcd = pTmpIcd;
pTrav->pAnchorNode = pAnchorNodeList;
// Count the number of sibling ICDs that are attributes versus elements
while (pTmpIcd)
{
if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE)
{
pTrav->uiSibIcdAttrs++;
}
else
{
pTrav->uiSibIcdElms++;
}
pTmpIcd = pTmpIcd->pNextSibling;
}
pTmpIcd = pTrav->pIcd->pPrevSibling;
while (pTmpIcd)
{
if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE)
{
pTrav->uiSibIcdAttrs++;
}
else
{
pTrav->uiSibIcdElms++;
}
pTmpIcd = pTmpIcd->pPrevSibling;
}
#ifdef FLM_DEBUG
if (pTrav->pIcd->uiDictNum == ELM_ROOT_TAG)
{
flmAssert( !pTrav->uiSibIcdAttrs && pTrav->uiSibIcdElms == 1);
flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE);
}
#endif
// See if we need to do this node's siblings. If it is an
// attribute node and there are no other attribute ICDs and
// no element ICDs, or if it is an element node and there are
// no other element ICDs and no attribute ICDs, we don't need
// to do the node's siblings. Otherwise, we do, so we will
// go up to the node's parent and back down to the first
// child element or first attribute.
pTrav->bTraverseSibs = FALSE;
if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE &&
(pTrav->uiSibIcdAttrs > 1 || pTrav->uiSibIcdElms)) ||
(pTrav->pNode->getNodeType() == ELEMENT_NODE &&
(pTrav->uiSibIcdAttrs || pTrav->uiSibIcdElms > 1)))
{
if (RC_BAD( rc = pTrav->pNode->getParentNode( this,
(IF_DOMNode **)&pTmpNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
}
else if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE &&
pTrav->uiSibIcdElms) ||
(pTrav->pNode->getNodeType() == ELEMENT_NODE &&
pTrav->uiSibIcdElms > 1))
{
if (RC_BAD( rc = pTmpNode->getFirstChild( this,
(IF_DOMNode **)&pTmpNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
// No sibling elements, do we need to do attributes?
if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE &&
pTrav->uiSibIcdAttrs > 1) ||
(pTrav->pNode->getNodeType() == ELEMENT_NODE &&
pTrav->uiSibIcdAttrs))
{
goto Get_First_Attribute;
}
}
else
{
pTrav->pNode->Release();
pTrav->pNode = pTmpNode;
pTrav->pNode->AddRef();
pTmpNode->Release();
pTmpNode = NULL;
pTrav->bTraverseSibs = TRUE;
}
}
else
{
Get_First_Attribute:
if (RC_BAD( rc = pTmpNode->getFirstAttribute( this,
(IF_DOMNode **)&pTmpNode)))
{
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
{
goto Exit;
}
rc = NE_XFLM_OK;
}
else
{
pTrav->pNode->Release();
pTrav->pNode = pTmpNode;
pTrav->pNode->AddRef();
pTmpNode->Release();
pTmpNode = NULL;
pTrav->bTraverseSibs = TRUE;
}
}
}
// Position to a sibling node to start on. At this point, we should
// be guaranteed to at least find the node we started on
if (pTrav->bTraverseSibs)
{
if (RC_BAD( rc = kyFindSibNode( this, pTrav, TRUE, &bGotNode,
&bHadRepeatingSib)))
{
goto Exit;
}
flmAssert( bGotNode);
}
else if (pTrav->pNode->getNodeType() == ATTRIBUTE_NODE)
{
pTrav->bTraverseChildren = FALSE;
}
else // ELEMENT_NODE
{
// If we have a child ICD, we need to traverse child nodes.
pTrav->bTraverseChildren = pTrav->pIcd->pFirstChild
? TRUE
: FALSE;
// If this is our primary anchor node, we need to see
// if there are repeating siblings. No need to do it
// in the other above cases, because if bTraversSibs is
// TRUE, it will happen inside kyFindSibNode, and if
// the node type is an attribute node, they should never
// have repeated siblings.
if (pTrav->pAnchorNode &&
pTrav->pAnchorNode->bSeeIfRepeatingSibs)
{
if (RC_BAD( rc = kySeeIfRepeatingSibs( this, pTrav->pNode,
&bHadRepeatingSib)))
{
goto Exit;
}
}
}
// Follow all links from the current node and ICD,
// populating CDL tables as we go.
if( pTrav->pNode->getNodeType() == eIxNodeType &&
pTrav->pNode->getNameId() == uiIxNodeName &&
pTrav->pNode->getIxNodeId() == ui64IxNodeId)
{
pTrav->bInNodeSubtree = TRUE;
}
for (;;)
{
pCdl = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList;
if (pCdl && !pCdl->pNode &&
pTrav->pNode->getParentId() &&
pCdl->ui64ParentId == pTrav->pNode->getParentId())
{
pCdl->pNode = pTrav->pNode;
pCdl->bInNodeSubtree = pTrav->bInNodeSubtree;
pCdl->pNode->AddRef();
}
else
{
if (RC_BAD( rc = m_tempPool.poolAlloc( sizeof( CDL),
(void **)&pCdl)))
{
goto Exit;
}
pCdl->pNode = pTrav->pNode;
pCdl->ui64ParentId = pTrav->pNode->getParentId();
pCdl->bInNodeSubtree = pTrav->bInNodeSubtree;
pCdl->pNode->AddRef();
pCdl->pNext = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList;
pCdlTbl [pTrav->pIcd->uiCdl].pCdlList = pCdl;
}
// Add "missing" place-holders for any child ICDs
pChildIcd = pTrav->pIcd->pFirstChild;
while (pChildIcd)
{
if (RC_BAD( rc = m_tempPool.poolAlloc(
sizeof( CDL), (void **)&pCdl)))
{
goto Exit;
}
pCdl->pNode = NULL;
pCdl->bInNodeSubtree = pTrav->bInNodeSubtree;
pCdl->ui64ParentId = pTrav->pNode->getNodeId();
pCdl->pNext = pCdlTbl [pChildIcd->uiCdl].pCdlList;
pCdlTbl [pChildIcd->uiCdl].pCdlList = pCdl;
pChildIcd = pChildIcd->pNextSibling;
}
Next_Node:
if (pTrav->bTraverseChildren)
{
bInNodeSubtree = pTrav->bInNodeSubtree;
if (RC_BAD( rc = kyFindChildNode( this, &m_tempPool,
&pTrav, &bGotNode, &bHadRepeatingSib)))
{
goto Exit;
}
if (!bGotNode)
{
goto Next_Node;
}
if( bInNodeSubtree ||
(pTrav->pNode->getNodeType() == eIxNodeType &&
pTrav->pNode->getNameId() == uiIxNodeName &&
pTrav->pNode->getIxNodeId() == ui64IxNodeId))
{
pTrav->bInNodeSubtree = TRUE;
}
}
else if (pTrav->bTraverseSibs)
{
// Here we are using bInNodeSubtree to indicate whether the entire
// sibling list is in the node's subtree. It could be that the
// current node is the subtree, but that does not mean that the
// entire sibling list is in the sub-tree.
if (!pTrav->bInNodeSubtree)
{
bInNodeSubtree = FALSE;
}
else
{
// Is this sibling list in the node subtree?
// If bInNodeSubtree is TRUE, and this is not the original
// sub-tree node, then this sibling list has to be subordinate
// to the original sub-tree node.
if( pTrav->pNode->getNodeType() != eIxNodeType ||
pTrav->pNode->getNameId() != uiIxNodeName ||
pTrav->pNode->getIxNodeId() != ui64IxNodeId)
{
bInNodeSubtree = TRUE;
}
else
{
bInNodeSubtree = FALSE;
}
}
if (RC_BAD( rc = kyFindSibNode( this, pTrav, FALSE, &bGotNode,
&bHadRepeatingSib)))
{
goto Exit;
}
if (!bGotNode)
{
goto Next_Node;
}
// If the entire sibling list was in the node sub-tree or this
// node is the subtree, then we are in the subtree.
if( bInNodeSubtree ||
(pTrav->pNode->getNodeType() == eIxNodeType &&
pTrav->pNode->getNameId() == uiIxNodeName &&
pTrav->pNode->getIxNodeId() == ui64IxNodeId))
{
pTrav->bInNodeSubtree = TRUE;
}
else
{
pTrav->bInNodeSubtree = FALSE;
}
}
else
{
if (pTrav->pParent)
{
pTrav->pNode->Release();
pTrav->pNode = NULL;
pTrav = pTrav->pParent;
pTrav->bTraverseChildren = FALSE;
goto Next_Node;
}
else
{
break;
}
}
}
// Generate the keys from the combinations of CDLs.
switch (eAction)
{
case IX_UNLINK_NODE:
{
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
TRUE, FALSE)))
{
goto Exit;
}
if (!bHadRepeatingSib)
{
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
FALSE, TRUE)))
{
goto Exit;
}
}
break;
}
case IX_LINK_NODE:
{
if( !bHadRepeatingSib)
{
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
FALSE, FALSE)))
{
goto Exit;
}
}
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
TRUE, TRUE)))
{
goto Exit;
}
break;
}
case IX_DEL_NODE_VALUE:
{
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
TRUE, FALSE)))
{
goto Exit;
}
break;
}
case IX_ADD_NODE_VALUE:
{
if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl,
TRUE, TRUE)))
{
goto Exit;
}
break;
}
default:
{
rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED);
goto Exit;
}
}
Exit:
// Release the nodes CDL table
kyReleaseCdls( pIxd, pCdlTbl);
// Release any nodes in the traversal list.
if (pTrav)
{
// Go to top of list
while (pTrav->pParent)
{
pTrav = pTrav->pParent;
}
// Visit each NODE_TRAV node and release whatever
// DOM node it is pointing to.
while (pTrav)
{
if (pTrav->pNode)
{
pTrav->pNode->Release();
pTrav->pNode = NULL;
}
pTrav = pTrav->pChild;
}
}
if (pParent)
{
pParent->Release();
}
return( rc);
}
/****************************************************************************
Desc: Routine that is called before and after changing a node - so we can
delete and add any keys from the database.
****************************************************************************/
RCODE F_Db::updateIndexKeys(
FLMUINT uiCollectionNum,
F_DOMNode * pIxNode,
IxAction eAction,
FLMBOOL bStartOfUpdate,
FLMBOOL * pbIsIndexed)
{
RCODE rc = NE_XFLM_OK;
FLMBOOL bIsRoot;
FLMBOOL bIsIndexed = FALSE;
ICD * pIcd;
FLMUINT uiIcdDictNum;
IXD * pIxd;
void * pvMark = NULL;
FLMUINT64 ui64DocumentID;
FLMUINT uiNameId;
F_DOMNode * pNode = NULL;
F_AttrElmInfo defInfo;
IxAction eTmpAction;
if( bStartOfUpdate)
{
if (RC_BAD( rc = krefCntrlCheck()))
{
goto Exit;
}
if (m_pOldNodeList)
{
m_pOldNodeList->resetList();
}
}
if( RC_BAD( rc = pIxNode->getNameId( this, &uiNameId)))
{
goto Exit;
}
if( !uiNameId)
{
goto Exit;
}
pNode = pIxNode;
pNode->AddRef();
// Lookup the node's ICD list
switch( pNode->getNodeType())
{
case ELEMENT_NODE:
{
if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo)))
{
goto Exit;
}
break;
}
case ATTRIBUTE_NODE:
{
if (RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo)))
{
goto Exit;
}
break;
}
case DATA_NODE:
{
// Need to operate on the parent node, which should be an element
// node.
if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo)))
{
goto Exit;
}
if (RC_BAD( rc = pNode->getParentNode( this, (IF_DOMNode **)&pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
// Data node not yet linked to a parent node, so
// it won't affect indexing.
rc = NE_XFLM_OK;
}
goto Exit;
}
// Name ID of element better be the same as what we found in the
// data node, and node type better be an element node.
flmAssert( pNode->getNameId() == uiNameId);
flmAssert( pNode->getNodeType() == ELEMENT_NODE);
// Action cannot be link/unlink on data nodes. Higher level needs
// to take care of. They should first delete keys on the
// parent node, then unlink/link the node, then call add keys on
// the parent node.
flmAssert( eAction != IX_LINK_NODE && eAction != IX_UNLINK_NODE);
break;
}
default:
{
// If the node is not an element or an attribute, it will not have
// any effect on indexing.
goto Exit;
}
}
bIsRoot = pNode->isRootNode();
if( (pIcd = defInfo.m_pFirstIcd) == NULL)
{
if( !bIsRoot || !m_pDict->m_pRootIcdList)
{
goto Exit;
}
else
{
pIcd = m_pDict->m_pRootIcdList;
}
}
// The node may be indexed so we need to process the ICD list
pvMark = m_tempPool.poolMark();
bIsIndexed = TRUE;
ui64DocumentID = pNode->getDocumentId();
// Process each index
for (;;)
{
pIxd = pIcd->pIxd;
uiIcdDictNum = pIcd->uiDictNum;
// See if this ICD's index is on the collection we are doing.
// Also, make sure if we are doing a specific index, that this is
// that index.
if (pIxd->uiCollectionNum != uiCollectionNum)
{
goto Next_Index;
}
// See if this document has been indexed for this index.
if (ui64DocumentID > pIxd->ui64LastDocIndexed)
{
goto Next_Index;
}
if ((eTmpAction = eAction) == IX_LINK_AND_ADD_NODE)
{
if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling)
{
if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent)
{
// If this ICD is not a key or data component, it has no
// effect on index keys if we are only modifying its value.
goto Next_Index;
}
else
{
eTmpAction = IX_ADD_NODE_VALUE;
}
}
else
{
eTmpAction = IX_LINK_NODE;
}
}
else if (eTmpAction == IX_LINK_NODE || eTmpAction == IX_UNLINK_NODE)
{
// IX_LINK_NODE and IX_UNLINK_NODE are called when a node is linked
// to its parent. Thus, if this ICD is the root ICD, it will have
// no effect on index keys.
if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling)
{
goto Next_Index;
}
}
else
{
if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent)
{
// If this ICD is not a key or data component, it has no
// effect on index keys if we are only modifying its value.
goto Next_Index;
}
}
// Generate index keys for this index.
if (RC_BAD( rc = genIndexKeys( ui64DocumentID, pNode, pIxd,
pIcd, eTmpAction)))
{
goto Exit;
}
Next_Index:
m_tempPool.poolReset( pvMark);
if ((pIcd = pIcd->pNextInChain) == NULL)
{
if (!bIsRoot || uiIcdDictNum == ELM_ROOT_TAG)
{
break;
}
// When done processing regular ICDs, process the
// root ICD list, if this node is a root node.
if ((pIcd = m_pDict->m_pRootIcdList) == NULL)
{
break;
}
}
}
Exit:
if( pvMark)
{
m_tempPool.poolReset( pvMark);
}
if (pNode)
{
pNode->Release();
}
if( pbIsIndexed)
{
*pbIsIndexed = bIsIndexed;
}
return( rc);
}