git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
3037 lines
62 KiB
C++
3037 lines
62 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Routines to perform dictionary updates.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1991-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: fslfileu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
typedef struct FTravTag * F_TRAV_p;
|
|
typedef struct FTravTag
|
|
{
|
|
F_DOMNode * pNode;
|
|
ICD * pRefIcd;
|
|
ICD * pIcd;
|
|
IX_CONTEXT * pIxContext;
|
|
F_TRAV_p pParent;
|
|
F_TRAV_p pChild;
|
|
} F_TRAV;
|
|
|
|
FSTATIC void kyFreeIxContext(
|
|
IXD * pIxd,
|
|
IX_CONTEXT * pIxContext,
|
|
IX_CONTEXT ** ppIxContextList);
|
|
|
|
/****************************************************************************
|
|
Desc: Check a dictionary definition for duplicate names or numbers.
|
|
If no number was assigned, assign one. If deleting, verify that the
|
|
delete is allowed at this point. Freeze certain attributes so
|
|
they cannot be changed.
|
|
****************************************************************************/
|
|
RCODE F_Db::checkDictDefInfo(
|
|
FLMUINT64 ui64DocumentID,
|
|
FLMBOOL bDeleting,
|
|
FLMUINT * puiDictType,
|
|
FLMUINT * puiDictNumber)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_DOMNode * pNode = NULL;
|
|
F_DOMNode * pAttr = NULL;
|
|
F_DOMNode * pTmpNode = NULL;
|
|
FLMBYTE szTmpBuf [80];
|
|
FLMUINT uiNameId;
|
|
F_DataVector key1;
|
|
F_DataVector key2;
|
|
FLMUNICODE * puzName = NULL;
|
|
FLMUNICODE * puzNamespace = NULL;
|
|
FLMUINT uiState = 0;
|
|
FLMBOOL bFoundState = FALSE;
|
|
FLMUINT uiMaxTagNum;
|
|
FLMBOOL bAmbiguous;
|
|
FLMBOOL bHasAttrs;
|
|
FLMUINT uiInsertPos;
|
|
FLM_TAG_INFO * pTagInfo;
|
|
|
|
*puiDictType = 0;
|
|
*puiDictNumber = 0;
|
|
|
|
// Get the root node of the definition document
|
|
|
|
if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNode->getNameId( this, puiDictType)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Ignore anything that is one one of our predefined types.
|
|
|
|
switch (*puiDictType)
|
|
{
|
|
case ELM_ELEMENT_TAG:
|
|
uiMaxTagNum = XFLM_MAX_ELEMENT_NUM;
|
|
break;
|
|
case ELM_ATTRIBUTE_TAG:
|
|
uiMaxTagNum = XFLM_MAX_ATTRIBUTE_NUM;
|
|
break;
|
|
case ELM_INDEX_TAG:
|
|
uiMaxTagNum = XFLM_MAX_INDEX_NUM;
|
|
break;
|
|
case ELM_PREFIX_TAG:
|
|
uiMaxTagNum = XFLM_MAX_PREFIX_NUM;
|
|
break;
|
|
case ELM_COLLECTION_TAG:
|
|
uiMaxTagNum = XFLM_MAX_COLLECTION_NUM;
|
|
break;
|
|
case ELM_ENCDEF_TAG:
|
|
#ifndef FLM_USE_NICI
|
|
rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE);
|
|
goto Exit;
|
|
#else
|
|
uiMaxTagNum = XFLM_MAX_ENCDEF_NUM;
|
|
break;
|
|
#endif
|
|
default:
|
|
*puiDictType = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNode->hasAttributes( this, &bHasAttrs)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( !bHasAttrs)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pNode->getNodeType() != ELEMENT_NODE)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr)))
|
|
{
|
|
if( rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = RC_SET( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Cycle through the attributes, pulling out stuff we are interested
|
|
// in.
|
|
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
switch (uiNameId)
|
|
{
|
|
case ATTR_NAME_TAG:
|
|
{
|
|
if (RC_BAD( rc = pAttr->getUnicode( this, &puzName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Put a freeze on name if we are not deleting.
|
|
// Modify is allowed, but delete is not.
|
|
|
|
if (!bDeleting)
|
|
{
|
|
if (RC_BAD( rc = pAttr->addModeFlags(
|
|
this, FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATTR_TARGET_NAMESPACE_TAG:
|
|
{
|
|
if (RC_BAD( rc = pAttr->getUnicode( this, &puzNamespace)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATTR_TYPE_TAG:
|
|
{
|
|
// Put a freeze on data type if we are not deleting.
|
|
|
|
if (!bDeleting)
|
|
{
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATTR_STATE_TAG:
|
|
{
|
|
if (RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf,
|
|
sizeof( szTmpBuf), 0, ~((FLMUINT)0))))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (*puiDictType == ELM_INDEX_TAG)
|
|
{
|
|
if (RC_BAD( rc = fdictGetIndexState(
|
|
(char *)szTmpBuf, &uiState)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (*puiDictType == ELM_ELEMENT_TAG ||
|
|
*puiDictType == ELM_ATTRIBUTE_TAG)
|
|
{
|
|
if (RC_BAD( rc = fdictGetState(
|
|
(char *)szTmpBuf, &uiState)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Put a freeze on state if we are not deleting.
|
|
|
|
if (!bDeleting &&
|
|
(*puiDictType == ELM_INDEX_TAG ||
|
|
*puiDictType == ELM_ELEMENT_TAG ||
|
|
*puiDictType == ELM_ATTRIBUTE_TAG))
|
|
{
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
bFoundState = TRUE;
|
|
break;
|
|
}
|
|
|
|
case ATTR_STATE_CHANGE_COUNT_TAG:
|
|
{
|
|
// Put a freeze on state change count if we are not deleting.
|
|
|
|
if (!bDeleting &&
|
|
(*puiDictType == ELM_ELEMENT_TAG ||
|
|
*puiDictType == ELM_ATTRIBUTE_TAG))
|
|
{
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATTR_DICT_NUMBER_TAG:
|
|
{
|
|
if (RC_BAD( rc = pAttr->getUINT( this, puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we are deleting, no need to verify or alter
|
|
// dictionary number.
|
|
|
|
if (bDeleting)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If the set dictionary number was zero, allocate a new
|
|
// one, set it, and freeze it.
|
|
|
|
if (!(*puiDictNumber))
|
|
{
|
|
if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType,
|
|
puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// *puiDictNumber will be zero coming back if the
|
|
// dictionary type does not keep track of a next
|
|
// dictionary number (like for prefixes)
|
|
|
|
if (*puiDictNumber)
|
|
{
|
|
if( RC_BAD( rc = pAttr->removeModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = pAttr->setUINT( this, *puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set the next unused dictionary number for this type - but only
|
|
// if this number is >= the one already stored.
|
|
|
|
if (RC_BAD( rc = m_pDict->setNextDictNum( this, *puiDictType,
|
|
*puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// Ignore all other attributes for now.
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr)))
|
|
{
|
|
if( rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If dictionary number is missing, create one and add it in.
|
|
|
|
if (!(*puiDictNumber) && !bDeleting)
|
|
{
|
|
|
|
// Allocate the next unused dictionary number for this type.
|
|
|
|
if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType,
|
|
puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// *puiDictNumber will be zero coming back if the
|
|
// dictionary type does not keep track of a next
|
|
// dictionary number for this type (like for prefixes)
|
|
|
|
if (*puiDictNumber)
|
|
{
|
|
|
|
// Create a dict number attribute, set it to the newly
|
|
// allocated value, and freeze it.
|
|
|
|
if (RC_OK( rc = pNode->createAttribute( this, ATTR_DICT_NUMBER_TAG,
|
|
(IF_DOMNode **)&pAttr)))
|
|
{
|
|
if (RC_OK( rc = pAttr->setUINT( this, *puiDictNumber)))
|
|
{
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bDeleting)
|
|
{
|
|
|
|
// Must have a name specified, and it must be unique
|
|
|
|
if (!puzName)
|
|
{
|
|
switch (*puiDictType)
|
|
{
|
|
case ELM_ELEMENT_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_ELEMENT_NAME);
|
|
break;
|
|
case ELM_ATTRIBUTE_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NAME);
|
|
break;
|
|
case ELM_INDEX_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME);
|
|
break;
|
|
case ELM_PREFIX_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_PREFIX_NAME);
|
|
break;
|
|
case ELM_COLLECTION_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NAME);
|
|
break;
|
|
case ELM_ENCDEF_TAG:
|
|
rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NAME);
|
|
break;
|
|
default:
|
|
|
|
// Should never hit this case!
|
|
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
goto Exit; // Will return NE_XFLM_OK
|
|
}
|
|
|
|
// Verify name uniqueness
|
|
|
|
key1.reset();
|
|
if (RC_BAD( rc = key1.setUINT( 0, *puiDictType)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = key1.setUnicode( 1, puzName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if ((*puiDictType == ELM_ELEMENT_TAG ||
|
|
*puiDictType == ELM_ATTRIBUTE_TAG) &&
|
|
puzNamespace)
|
|
{
|
|
if (RC_BAD( rc = key1.setUnicode( 2, puzNamespace)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX,
|
|
&key1, XFLM_EXACT, &key2)))
|
|
{
|
|
if (rc == NE_XFLM_NOT_FOUND)
|
|
{
|
|
// We should have found the thing! It should have
|
|
// already been indexed!
|
|
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// See if there is another one after this for the same
|
|
// key.
|
|
|
|
if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX,
|
|
&key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS,
|
|
&key1)))
|
|
{
|
|
if (rc != NE_XFLM_EOF_HIT)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = NE_XFLM_OK;
|
|
|
|
// See if it is defined in our name table and is a number in
|
|
// our reserved range. In that case, it should never be
|
|
// coming through here - it is a name conflict we should prevent.
|
|
|
|
if ((pTagInfo = m_pDict->getNameTable()->findTagByTypeAndName(
|
|
*puiDictType, puzName, NULL, TRUE,
|
|
puzNamespace, &bAmbiguous, &uiInsertPos)) != NULL)
|
|
{
|
|
if (pTagInfo->uiTagNum > uiMaxTagNum)
|
|
{
|
|
goto Have_Name_Conflict;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Have_Name_Conflict:
|
|
|
|
// We should NOT have found another one with this same name
|
|
|
|
switch (*puiDictType)
|
|
{
|
|
case ELM_ELEMENT_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NAME);
|
|
goto Exit;
|
|
case ELM_ATTRIBUTE_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NAME);
|
|
goto Exit;
|
|
case ELM_INDEX_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NAME);
|
|
goto Exit;
|
|
case ELM_COLLECTION_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NAME);
|
|
goto Exit;
|
|
case ELM_PREFIX_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_PREFIX_NAME);
|
|
goto Exit;
|
|
default:
|
|
|
|
// VISIT: Do we care on other dictionary types that
|
|
// we have a duplicate name?
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If this is an attribute definition, and the name is "xmlns" or
|
|
// begins with "xmlns:", it cannot have a target namespace.
|
|
|
|
if (*puiDictType == ELM_ATTRIBUTE_TAG &&
|
|
puzNamespace && *puzNamespace &&
|
|
isXMLNS( puzName) &&
|
|
(puzName [5] == 0 || (puzName [5] == ':' && puzName [6])))
|
|
{
|
|
rc = RC_SET( NE_XFLM_NAMESPACE_NOT_ALLOWED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Verify dictionary number uniqueness
|
|
|
|
if (*puiDictNumber && !bDeleting)
|
|
{
|
|
key1.reset();
|
|
key2.reset();
|
|
if (RC_BAD( rc = key1.setUINT( 0, *puiDictType)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = key1.setUINT( 1, *puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX,
|
|
&key1, XFLM_EXACT, &key2)))
|
|
{
|
|
if (rc == NE_XFLM_NOT_FOUND)
|
|
{
|
|
// We should have found the thing! It should have
|
|
// already been indexed!
|
|
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// See if there is another one after this for the same
|
|
// key.
|
|
|
|
if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX,
|
|
&key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS,
|
|
&key1)))
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// We should NOT have found another one with this same number
|
|
|
|
switch (*puiDictType)
|
|
{
|
|
case ELM_ELEMENT_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NUM);
|
|
goto Exit;
|
|
case ELM_ATTRIBUTE_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NUM);
|
|
goto Exit;
|
|
case ELM_INDEX_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NUM);
|
|
goto Exit;
|
|
case ELM_COLLECTION_TAG:
|
|
rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NUM);
|
|
goto Exit;
|
|
default:
|
|
|
|
// Dictionary number is not used for other types
|
|
// so we really don't care if there is a duplicate
|
|
// in these cases.
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Is it a delete?
|
|
|
|
if (bDeleting)
|
|
{
|
|
if (*puiDictType == ELM_ELEMENT_TAG)
|
|
{
|
|
if (*puiDictNumber)
|
|
{
|
|
// Make sure that elements are in the right state
|
|
// to be deleted.
|
|
|
|
if( !m_bItemStateUpdOk)
|
|
{
|
|
rc = RC_SET( NE_XFLM_CANNOT_DEL_ELEMENT);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if (*puiDictType == ELM_ATTRIBUTE_TAG)
|
|
{
|
|
if (*puiDictNumber)
|
|
{
|
|
// Make sure that attributes are in the right state
|
|
// to be deleted.
|
|
|
|
if( !m_bItemStateUpdOk)
|
|
{
|
|
rc = RC_SET( NE_XFLM_CANNOT_DEL_ATTRIBUTE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if (*puiDictType == ELM_COLLECTION_TAG)
|
|
{
|
|
if (*puiDictNumber)
|
|
{
|
|
if (RC_BAD( rc = m_pDict->checkCollectionReferences( *puiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set a state attribute and freeze it if it was missing.
|
|
// This makes it so that the state can only be changed by a call
|
|
// to changeItemState. This routine ensures that the state
|
|
// change is legal.
|
|
|
|
if( (*puiDictType == ELM_ATTRIBUTE_TAG ||
|
|
*puiDictType == ELM_ELEMENT_TAG ||
|
|
*puiDictType == ELM_ENCDEF_TAG) && !bFoundState)
|
|
{
|
|
if (RC_BAD( rc = pNode->createAttribute(
|
|
this, ATTR_STATE_TAG, (IF_DOMNode **)&pTmpNode)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = pTmpNode->setUTF8(
|
|
this, (FLMBYTE *)XFLM_ACTIVE_OPTION_STR)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pTmpNode->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
|
|
if (pAttr)
|
|
{
|
|
pAttr->Release();
|
|
}
|
|
|
|
if (pTmpNode)
|
|
{
|
|
pTmpNode->Release();
|
|
}
|
|
|
|
if (puzName)
|
|
{
|
|
f_free( &puzName);
|
|
}
|
|
|
|
if (puzNamespace)
|
|
{
|
|
f_free( &puzNamespace);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine is called when a dictionary document is done being
|
|
modified. It will check the definition and then generate any
|
|
dictionary updates that need to be done.
|
|
****************************************************************************/
|
|
RCODE F_Db::dictDocumentDone(
|
|
FLMUINT64 ui64DocumentID,
|
|
FLMBOOL bDeleting,
|
|
FLMUINT * puiDictDefType)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiDictType;
|
|
FLMUINT uiDictNumber;
|
|
|
|
if (puiDictDefType)
|
|
{
|
|
*puiDictDefType = 0;
|
|
}
|
|
|
|
// Document ID 1 is the one that is reserved for next element, next
|
|
// attribute, next index, and next collection.
|
|
|
|
if (ui64DocumentID == XFLM_DICTINFO_DOC_ID)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Flush any index keys before making any changes to dictionary items.
|
|
// Dictionary changes may change index definitions, etc.
|
|
|
|
if( RC_BAD( rc = keysCommit( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Clear out the cdl table in case it changes.
|
|
|
|
krefCntrlFree();
|
|
|
|
// Retrieve the root element of the definition
|
|
|
|
if( RC_BAD( rc = checkDictDefInfo( ui64DocumentID, bDeleting,
|
|
&uiDictType, &uiDictNumber)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
|
|
// Was the document one of our known dictionary document types?
|
|
// Also, had a dictionary number been defined. If bDeleting
|
|
// is TRUE and no dictionary number had been defined, there
|
|
// is nothing to be done on the internal dictionary.
|
|
|
|
if (!uiDictType || !uiDictNumber)
|
|
{
|
|
goto Exit; // Will return NE_XFLM_OK
|
|
}
|
|
|
|
// Create a separate dictionary object if one has not already
|
|
// been created.
|
|
|
|
if (!(m_uiFlags & FDB_UPDATED_DICTIONARY))
|
|
{
|
|
if (RC_BAD( rc = dictClone()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Update the dictionary and create/drop indexes or containers
|
|
// if it is that type of definition
|
|
|
|
if (RC_BAD( rc = m_pDict->updateDict( this, uiDictType, ui64DocumentID,
|
|
uiDictNumber, FALSE, bDeleting)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Return the type of definition
|
|
|
|
if( puiDictDefType)
|
|
{
|
|
*puiDictDefType = uiDictType;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
setMustAbortTrans( rc);
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Copies an existing dictionary to a new dictionary.
|
|
****************************************************************************/
|
|
RCODE F_Db::dictClone( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_Dict * pNewDict = NULL;
|
|
|
|
// Allocate a new FDICT structure
|
|
|
|
if ((pNewDict = f_new F_Dict) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
// Nothing to do is not a legal state.
|
|
|
|
if (!m_pDict)
|
|
{
|
|
flmAssert( 0);
|
|
m_pDict = pNewDict;
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy the dictionary.
|
|
|
|
if (RC_BAD( rc = pNewDict->cloneDict( m_pDict)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pDatabase->lockMutex();
|
|
unlinkFromDict();
|
|
m_pDatabase->unlockMutex();
|
|
m_pDict = pNewDict;
|
|
pNewDict = NULL;
|
|
m_uiFlags |= FDB_UPDATED_DICTIONARY;
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc) && pNewDict)
|
|
{
|
|
pNewDict->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Build an index.
|
|
****************************************************************************/
|
|
RCODE F_Db::buildIndex(
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT uiState)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
LFILE * pIxLFile;
|
|
IXD * pIxd;
|
|
|
|
// Flush any KY keys and free the tables because they may grow!
|
|
|
|
if (RC_BAD( rc = keysCommit( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = krefCntrlCheck()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD(rc = m_pDict->getIndex( uiIndexNum, &pIxLFile, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NON-BLOCKING INDEX BUILD - NOTE: The IXD_SUSPENDED flag may
|
|
// also be set, which indicates that we should NOT start the
|
|
// background maintenance thread right now.
|
|
|
|
if (uiState & IXD_OFFLINE)
|
|
{
|
|
if (RC_BAD( rc = setIxStateInfo( pIxd->uiIndexNum, 0, uiState)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// setIxStateInfo may have changed to a new dictionary, so pIxd is no
|
|
// good after this point
|
|
|
|
pIxd = NULL;
|
|
|
|
// Don't schedule a maintenance thread if index is to start
|
|
// out life in a suspended state, or if we are replaying
|
|
// the roll-forward log.
|
|
|
|
if (!(uiState & IXD_SUSPENDED) && !(m_uiFlags & FDB_REPLAYING_RFL))
|
|
{
|
|
if (RC_BAD( rc = addToStartList( uiIndexNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Done
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// There may be "new" nodes in the node cache.
|
|
// Need to flush them to the database so that
|
|
// the B-Tree lookups done by the indexing code will
|
|
// work correctly
|
|
|
|
if( RC_BAD( rc = flushDirtyNodes()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NORMAL INDEX BUILD - BLOCKING. uiIndexToBeUpdated better be
|
|
// zero at this point since we are not working in the background.
|
|
|
|
if (RC_BAD( rc = indexSetOfDocuments( uiIndexNum, 1,
|
|
~((FLMUINT64)0), m_pIxStatus, m_pIxClient, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Logs information about an index being built
|
|
****************************************************************************/
|
|
void flmLogIndexingProgress(
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT64 ui64LastDocumentId)
|
|
{
|
|
IF_LogMessageClient * pLogMsg = NULL;
|
|
char szMsg[ 128];
|
|
|
|
if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL)
|
|
{
|
|
pLogMsg->changeColor( XFLM_YELLOW, XFLM_BLACK);
|
|
if (ui64LastDocumentId)
|
|
{
|
|
f_sprintf( (char *)szMsg,
|
|
"Indexing progress: Index %u is offline. Last document processed = %I64u.",
|
|
(unsigned)uiIndexNum, ui64LastDocumentId);
|
|
}
|
|
else
|
|
{
|
|
f_sprintf( (char *)szMsg,
|
|
"Indexing progress: Index %u is online.",
|
|
(unsigned)uiIndexNum);
|
|
}
|
|
pLogMsg->appendString( szMsg);
|
|
}
|
|
flmEndLogMessage( &pLogMsg);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Unlink and free an IX_CONTEXT structure.
|
|
****************************************************************************/
|
|
FSTATIC void kyFreeIxContext(
|
|
IXD * pIxd,
|
|
IX_CONTEXT * pIxContext,
|
|
IX_CONTEXT ** ppIxContextList
|
|
)
|
|
{
|
|
if (pIxContext->pPrev)
|
|
{
|
|
pIxContext->pPrev->pNext = pIxContext->pNext;
|
|
}
|
|
else
|
|
{
|
|
*ppIxContextList = pIxContext->pNext;
|
|
}
|
|
if (pIxContext->pNext)
|
|
{
|
|
pIxContext->pNext->pPrev = pIxContext->pPrev;
|
|
}
|
|
kyReleaseCdls( pIxd, pIxContext->pCdlTbl);
|
|
if (pIxContext->pPool)
|
|
{
|
|
pIxContext->pPool->poolFree();
|
|
pIxContext->pPool->Release();
|
|
}
|
|
f_free( &pIxContext);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Output the keys for a particular IX_CONTEXT structure. Also, free
|
|
the IX_CONTEXT structure and its associated CDLs, etc.
|
|
****************************************************************************/
|
|
RCODE F_Db::outputContextKeys(
|
|
FLMUINT64 ui64DocumentId,
|
|
IXD * pIxd,
|
|
IX_CONTEXT * pIxContext,
|
|
IX_CONTEXT ** ppIxContextList)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd,
|
|
pIxContext->pCdlTbl, TRUE, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Free the IX_CONTEXT structure - unlinks from list too.
|
|
|
|
kyFreeIxContext( pIxd, pIxContext, ppIxContextList);
|
|
|
|
// Flush keys if over threshhold - get key count before flushing.
|
|
|
|
if( pIxd->uiIndexNum && isKrefOverThreshold())
|
|
{
|
|
processDupKeys( pIxd);
|
|
if (RC_BAD( rc = keysCommit( FALSE, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Output the keys for a particular IX_CONTEXT structure and remove CDLs
|
|
for the specified ICD, if any.
|
|
****************************************************************************/
|
|
RCODE F_Db::removeCdls(
|
|
FLMUINT64 ui64DocumentId,
|
|
IXD * pIxd,
|
|
IX_CONTEXT * pIxContext,
|
|
ICD * pRefIcd)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
CDL * pCdl;
|
|
CDL * pOldCdlList;
|
|
ICD * pIcd;
|
|
|
|
if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd,
|
|
pIxContext->pCdlTbl, TRUE, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pIcd = pRefIcd;
|
|
while (pIcd)
|
|
{
|
|
|
|
// Free the CDLs for the specified ICD, if any.
|
|
// Put the CDLs into a table for reuse.
|
|
|
|
pCdl = pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList;
|
|
pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList = NULL;
|
|
if (pCdl)
|
|
{
|
|
pOldCdlList = pIxContext->pCdlList;
|
|
pIxContext->pCdlList = pCdl;
|
|
for (;;)
|
|
{
|
|
if (pCdl->pNode)
|
|
{
|
|
pCdl->pNode->Release();
|
|
pCdl->pNode = NULL;
|
|
}
|
|
if (!pCdl->pNext)
|
|
{
|
|
pCdl->pNext = pOldCdlList;
|
|
break;
|
|
}
|
|
pCdl = pCdl->pNext;
|
|
}
|
|
}
|
|
if (pIcd == pRefIcd->pParent)
|
|
{
|
|
break;
|
|
}
|
|
if ((pIcd = pIcd->pNextSibling) == NULL)
|
|
{
|
|
|
|
// Also do reference ICD's parent ICD. This is not absolutely
|
|
// necessary, since this CDLs will be ignored when we verify
|
|
// context, but we might as well do it to save just a little more
|
|
// memory space.
|
|
|
|
if ((pIcd = pRefIcd->pParent) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flush keys if over threshhold - get key count before flushing.
|
|
|
|
if( pIxd->uiIndexNum && isKrefOverThreshold())
|
|
{
|
|
processDupKeys( pIxd);
|
|
if (RC_BAD( rc = keysCommit( FALSE, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Index a particular document for a particular index.
|
|
****************************************************************************/
|
|
RCODE F_Db::indexDocument(
|
|
IXD * pIxd,
|
|
F_DOMNode * pDocNode)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
ICD * pIcd;
|
|
ICD * pChildIcd;
|
|
CDL * pCdl;
|
|
IX_CONTEXT * pIxContextList = NULL;
|
|
IX_CONTEXT * pIxContext;
|
|
F_DOMNode * pTmpNode = NULL;
|
|
F_TRAV * pTrav = NULL;
|
|
void * pvMark = m_TempPool.poolMark();
|
|
CDL_HDR * pCdlHdr;
|
|
FLMUINT64 ui64DocId;
|
|
|
|
// Root of a document cannot be an attribute node
|
|
|
|
flmAssert( pDocNode->getNodeType() != ATTRIBUTE_NODE);
|
|
|
|
// Get the document ID
|
|
|
|
if( RC_BAD( rc = pDocNode->getNodeId( this, &ui64DocId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If the index number is zero, we are generating keys for a query
|
|
// but not to store in the database. We want the kref table cleared
|
|
// out.
|
|
|
|
if (!pIxd->uiIndexNum)
|
|
{
|
|
if (!m_bKrefSetup)
|
|
{
|
|
if (RC_BAD( rc = krefCntrlCheck()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (m_eTransType == XFLM_UPDATE_TRANS)
|
|
{
|
|
if (RC_BAD( rc = keysCommit( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Empty the table out so that the only keys added in this
|
|
// call are for this index, this document.
|
|
|
|
m_pKrefPool->poolReset( NULL, TRUE);
|
|
m_uiKrefCount = 0;
|
|
m_uiTotalKrefBytes = 0;
|
|
}
|
|
flmAssert( !m_uiKrefCount && !m_uiTotalKrefBytes);
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = krefCntrlCheck()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do an in-order traversal of the document.
|
|
|
|
if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( F_TRAV), (void **)&pTrav)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pTrav->pNode = pDocNode;
|
|
pTrav->pNode->AddRef();
|
|
// pTrav->pRefIcd = NULL; // Set by poolCalloc
|
|
// pTrav->pIcd = NULL; // Set by poolCalloc
|
|
// pTrav->pIxContext = NULL; // Set by poolCalloc
|
|
// pTrav->pParent = NULL; // Set by poolCalloc
|
|
// pTrav->pChild = NULL; // Set by poolCalloc
|
|
|
|
for (;;)
|
|
{
|
|
FLMUINT uiNameId;
|
|
eDomNodeType eNodeType = pTrav->pNode->getNodeType();
|
|
|
|
if( RC_BAD( rc = pTrav->pNode->getNameId( this, &uiNameId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiNameId &&
|
|
(eNodeType == ELEMENT_NODE || eNodeType == ATTRIBUTE_NODE))
|
|
{
|
|
FLMBOOL bCheckedIcdTreeRoot = FALSE;
|
|
|
|
// See if the node has an ICD in the current context.
|
|
|
|
if ((pIcd = pTrav->pRefIcd) == NULL)
|
|
{
|
|
pIcd = pIxd->pIcdTree;
|
|
bCheckedIcdTreeRoot = TRUE;
|
|
}
|
|
if (pIcd->uiDictNum == ELM_ROOT_TAG)
|
|
{
|
|
if (pTrav->pNode->getParentId())
|
|
{
|
|
pIcd = NULL;
|
|
}
|
|
}
|
|
else if (eNodeType == ELEMENT_NODE)
|
|
{
|
|
while (pIcd && (pIcd->uiDictNum != uiNameId ||
|
|
(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
|
|
{
|
|
pIcd = pIcd->pNextSibling;
|
|
}
|
|
|
|
// If we did not start at the root of the ICD tree,
|
|
// need to go back there to see if we need to start
|
|
// a new context. However, if the root if the ICD tree
|
|
// is ELM_ROOT_TAG, it is pointless, because the current
|
|
// pTrev->pNode should be a child of some node if
|
|
// bCheckedIcdTreeRoot is FALSE.
|
|
|
|
if (!pIcd && !bCheckedIcdTreeRoot &&
|
|
pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG)
|
|
{
|
|
pIcd = pIxd->pIcdTree;
|
|
while (pIcd && (pIcd->uiDictNum != uiNameId ||
|
|
(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
|
|
{
|
|
pIcd = pIcd->pNextSibling;
|
|
}
|
|
if (pIcd)
|
|
{
|
|
|
|
// Reset these so that a new context will be
|
|
// created below.
|
|
|
|
pTrav->pRefIcd = NULL;
|
|
pTrav->pIxContext = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (pIcd && (pIcd->uiDictNum != uiNameId ||
|
|
!(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
|
|
{
|
|
pIcd = pIcd->pNextSibling;
|
|
}
|
|
|
|
// If we did not start at the root of the ICD tree,
|
|
// need to go back there to see if we need to start
|
|
// a new context. However, if the root if the ICD tree
|
|
// is ELM_ROOT_TAG, it is pointless, because the current
|
|
// pTrev->pNode should be a child of some node if
|
|
// bCheckedIcdTreeRoot is FALSE.
|
|
|
|
if (!pIcd && !bCheckedIcdTreeRoot &&
|
|
pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG)
|
|
{
|
|
pIcd = pIxd->pIcdTree;
|
|
while (pIcd && (pIcd->uiDictNum != uiNameId ||
|
|
!(pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
|
|
{
|
|
pIcd = pIcd->pNextSibling;
|
|
}
|
|
if (pIcd)
|
|
{
|
|
|
|
// Reset these so that a new context will be
|
|
// created below.
|
|
|
|
pTrav->pRefIcd = NULL;
|
|
pTrav->pIxContext = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found a matching ICD, see if we want to save the node
|
|
// in a CDL table.
|
|
|
|
if ((pTrav->pIcd = pIcd) != NULL)
|
|
{
|
|
|
|
// If there is no indexing context, start one.
|
|
|
|
if (!pTrav->pIxContext)
|
|
{
|
|
flmAssert( !pTrav->pRefIcd);
|
|
if (RC_BAD( rc = f_calloc( sizeof( IX_CONTEXT), &pIxContext)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if ((pIxContext->pNext = pIxContextList) != NULL)
|
|
{
|
|
pIxContextList->pPrev = pIxContext;
|
|
}
|
|
pIxContextList = pIxContext;
|
|
|
|
if ((pIxContext->pPool = f_new F_Pool) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
pIxContext->pPool->poolInit( 512);
|
|
|
|
if (RC_BAD( rc = pIxContext->pPool->poolCalloc(
|
|
sizeof( CDL_HDR) * pIxd->uiNumIcds,
|
|
(void **)&pIxContext->pCdlTbl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pTrav->pIxContext = pIxContext;
|
|
pTrav->pRefIcd = pIxd->pIcdTree;
|
|
}
|
|
else
|
|
{
|
|
pIxContext = pTrav->pIxContext;
|
|
}
|
|
|
|
// If this node has a parent, and the front of the
|
|
// list is a "missing" place holder for the node,
|
|
// replace that CDL.
|
|
|
|
pCdlHdr = &pIxContext->pCdlTbl [pIcd->uiCdl];
|
|
pCdl = pCdlHdr->pCdlList;
|
|
if (pCdl && !pCdl->pNode &&
|
|
pTrav->pNode->getParentId() &&
|
|
pCdl->ui64ParentId == pTrav->pNode->getParentId())
|
|
{
|
|
pCdl->pNode = pTrav->pNode;
|
|
pCdl->bInNodeSubtree = TRUE;
|
|
pCdl->pNode->AddRef();
|
|
}
|
|
else
|
|
{
|
|
|
|
// Reuse a CDL if one is available.
|
|
|
|
if (pIxContext->pCdlList)
|
|
{
|
|
pCdl = pIxContext->pCdlList;
|
|
pIxContext->pCdlList = pCdl->pNext;
|
|
|
|
// pCdl->pNode should have been released when it was
|
|
// put into the CDL list!
|
|
|
|
flmAssert( !pCdl->pNode);
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = pIxContext->pPool->poolAlloc(
|
|
sizeof( CDL), (void **)&pCdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pCdl->pNode = pTrav->pNode;
|
|
pCdl->ui64ParentId = pTrav->pNode->getParentId();
|
|
pCdl->bInNodeSubtree = TRUE;
|
|
pCdl->pNode->AddRef();
|
|
pCdl->pNext = pCdlHdr->pCdlList;
|
|
pCdlHdr->pCdlList = pCdl;
|
|
}
|
|
|
|
// Add "missing" place-holders for any child ICDs
|
|
|
|
pChildIcd = pIcd->pFirstChild;
|
|
while (pChildIcd)
|
|
{
|
|
|
|
// Reuse a CDL if one is available.
|
|
|
|
if (pIxContext->pCdlList)
|
|
{
|
|
pCdl = pIxContext->pCdlList;
|
|
pIxContext->pCdlList = pCdl->pNext;
|
|
|
|
// pCdl->pNode should have been released when it was
|
|
// put into the CDL list!
|
|
|
|
flmAssert( !pCdl->pNode);
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = pIxContext->pPool->poolAlloc(
|
|
sizeof( CDL), (void **)&pCdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = pTrav->pNode->getNodeId(
|
|
this, &pCdl->ui64ParentId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pCdl->pNode = NULL;
|
|
pCdl->bInNodeSubtree = TRUE;
|
|
pCdlHdr = &pIxContext->pCdlTbl [pChildIcd->uiCdl];
|
|
pCdl->pNext = pCdlHdr->pCdlList;
|
|
pCdlHdr->pCdlList = pCdl;
|
|
pChildIcd = pChildIcd->pNextSibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go to the next node.
|
|
|
|
if( eNodeType == ATTRIBUTE_NODE)
|
|
{
|
|
if (RC_OK( rc = pTrav->pNode->getNextSibling( this,
|
|
(IF_DOMNode **)&pTrav->pNode)))
|
|
{
|
|
pTrav->pIcd = NULL;
|
|
continue;
|
|
}
|
|
else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
|
|
// No siblings, go back to parent node and see if it has any
|
|
// siblings.
|
|
|
|
if (pTrav->pNode)
|
|
{
|
|
pTrav->pNode->Release();
|
|
pTrav->pNode = NULL;
|
|
}
|
|
|
|
// If the parent has a different IX_CONTEXT (including NULL)
|
|
// free this one before going back to the parent.
|
|
|
|
if (pTrav->pIxContext && pTrav->pParent &&
|
|
pTrav->pParent->pIxContext != pTrav->pIxContext)
|
|
{
|
|
if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd,
|
|
pTrav->pIxContext, &pIxContextList)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pTrav->pIxContext = NULL;
|
|
pTrav->pRefIcd = NULL;
|
|
}
|
|
else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH)
|
|
{
|
|
if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext,
|
|
pTrav->pRefIcd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pTrav = pTrav->pParent;
|
|
|
|
// Has to be a parent at this point!
|
|
|
|
flmAssert( pTrav);
|
|
|
|
// Fall through to do element's siblings.
|
|
}
|
|
else if (eNodeType == ELEMENT_NODE || eNodeType == DOCUMENT_NODE)
|
|
{
|
|
if (RC_OK( rc = pTrav->pNode->getFirstChild( this, (IF_DOMNode **)&pTmpNode)))
|
|
{
|
|
Setup_Child:
|
|
if (!pTrav->pChild)
|
|
{
|
|
F_TRAV * pNewTrav;
|
|
|
|
if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( F_TRAV),
|
|
(void **)&pNewTrav)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pNewTrav->pParent = pTrav;
|
|
pTrav->pChild = pNewTrav;
|
|
}
|
|
pTrav = pTrav->pChild;
|
|
if (pTrav->pNode)
|
|
{
|
|
pTrav->pNode->Release();
|
|
pTrav->pNode = NULL;
|
|
}
|
|
pTrav->pNode = pTmpNode;
|
|
pTrav->pNode->AddRef();
|
|
pTmpNode->Release();
|
|
pTmpNode = NULL;
|
|
pTrav->pRefIcd = (pTrav->pParent->pIcd
|
|
? pTrav->pParent->pIcd->pFirstChild
|
|
: NULL);
|
|
pTrav->pIcd = NULL;
|
|
pTrav->pIxContext = pTrav->pParent->pIxContext;
|
|
if (!pTrav->pRefIcd)
|
|
{
|
|
pTrav->pIxContext = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
|
|
// See if the node has any attributes
|
|
|
|
if( pTrav->pNode->getNodeType() == ELEMENT_NODE)
|
|
{
|
|
if (RC_OK( rc = pTrav->pNode->getFirstAttribute( this,
|
|
(IF_DOMNode **)&pTmpNode)))
|
|
{
|
|
goto Setup_Child;
|
|
}
|
|
else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Follow sibling chain. Go to parents until we find a
|
|
// parent that has a sibling.
|
|
|
|
for (;;)
|
|
{
|
|
// If the parent has a different IX_CONTEXT
|
|
// free this one before going back to the parent.
|
|
|
|
if (pTrav->pIxContext && pTrav->pParent && pTrav->pParent->pIxContext &&
|
|
pTrav->pParent->pIxContext != pTrav->pIxContext)
|
|
{
|
|
if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd,
|
|
pTrav->pIxContext, &pIxContextList)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pTrav->pRefIcd = (pTrav->pParent->pIcd
|
|
? pTrav->pParent->pIcd->pFirstChild
|
|
: NULL);
|
|
pTrav->pIcd = NULL;
|
|
pTrav->pIxContext = pTrav->pParent->pIxContext;
|
|
if (!pTrav->pRefIcd)
|
|
{
|
|
pTrav->pIxContext = NULL;
|
|
}
|
|
}
|
|
if (RC_BAD( rc = pTrav->pNode->getNextSibling( this, (IF_DOMNode **)&pTrav->pNode)))
|
|
{
|
|
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
rc = NE_XFLM_OK;
|
|
|
|
// If there is no parent node, we are done.
|
|
|
|
if (!pTrav->pParent)
|
|
{
|
|
goto Done_With_Document;
|
|
}
|
|
|
|
// See if the parent has a a first attribute.
|
|
|
|
if( pTrav->pParent->pNode->getNodeType() == ELEMENT_NODE)
|
|
{
|
|
if (RC_BAD( rc = pTrav->pParent->pNode->getFirstAttribute( this,
|
|
(IF_DOMNode **)&pTrav->pNode)))
|
|
{
|
|
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Will continue processing attributes as siblings.
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// At this point we will be going back to the parent node.
|
|
|
|
if (pTrav->pNode)
|
|
{
|
|
pTrav->pNode->Release();
|
|
pTrav->pNode = NULL;
|
|
}
|
|
|
|
// If the parent has no context, we need to output the keys we may
|
|
// have collected so far. NOTE: If parent's context is non-NULL and
|
|
// just different than our context, we will already have output the
|
|
// keys above. pTrav->pParent must be non-NULL at this point.
|
|
|
|
if (pTrav->pIxContext && !pTrav->pParent->pIxContext)
|
|
{
|
|
if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd,
|
|
pTrav->pIxContext, &pIxContextList)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH)
|
|
{
|
|
if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext,
|
|
pTrav->pRefIcd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pTrav = pTrav->pParent;
|
|
}
|
|
else if (pTrav->pNode->getNodeType() == ELEMENT_NODE)
|
|
{
|
|
pTrav->pIcd = NULL;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
// better not be in a list of attribute nodes at this point!
|
|
|
|
flmAssert( pTrav->pNode->getNodeType() != ATTRIBUTE_NODE);
|
|
}
|
|
}
|
|
}
|
|
|
|
Done_With_Document:
|
|
|
|
// Need to build keys for each index context that we have.
|
|
|
|
while (pIxContextList)
|
|
{
|
|
if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd,
|
|
pIxContextList, &pIxContextList)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Flush keys - get key count before flushing.
|
|
|
|
if (!pIxd->uiIndexNum)
|
|
{
|
|
processDupKeys( pIxd);
|
|
}
|
|
else
|
|
{
|
|
if( isKrefOverThreshold())
|
|
{
|
|
processDupKeys( pIxd);
|
|
if (RC_BAD( rc = keysCommit( FALSE, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pTmpNode)
|
|
{
|
|
pTmpNode->Release();
|
|
}
|
|
|
|
while (pTrav && pTrav->pParent)
|
|
{
|
|
pTrav = pTrav->pParent;
|
|
}
|
|
while (pTrav)
|
|
{
|
|
if (pTrav->pNode)
|
|
{
|
|
pTrav->pNode->Release();
|
|
pTrav->pNode = NULL;
|
|
}
|
|
pTrav = pTrav->pChild;
|
|
}
|
|
|
|
while (pIxContextList)
|
|
{
|
|
kyFreeIxContext( pIxd, pIxContextList, &pIxContextList);
|
|
}
|
|
|
|
m_TempPool.poolReset( pvMark);
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Index a set of documents or until time runs out.
|
|
****************************************************************************/
|
|
RCODE F_Db::indexSetOfDocuments(
|
|
FLMUINT uiIxNum,
|
|
FLMUINT64 ui64StartDocumentId,
|
|
FLMUINT64 ui64EndDocumentId,
|
|
IF_IxStatus * ifpIxStatus,
|
|
IF_IxClient * ifpIxClient,
|
|
XFLM_INDEX_STATUS * pIndexStatus,
|
|
FLMBOOL * pbHitEnd,
|
|
F_Thread * pThread)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT64 ui64DocumentId;
|
|
FLMUINT64 ui64LastDocumentId = 0;
|
|
IXD * pIxd = NULL;
|
|
F_COLLECTION * pCollection;
|
|
ServerLockObject *
|
|
pDatabaseLockObj = m_pDatabase->m_pDatabaseLockObj;
|
|
FLMBOOL bHitEnd = FALSE;
|
|
FLMUINT uiCurrTime;
|
|
FLMUINT uiLastStatusTime = 0;
|
|
FLMUINT uiStartTime;
|
|
FLMUINT uiMinTU;
|
|
FLMUINT uiStatusIntervalTU;
|
|
FLMUINT64 ui64DocumentsProcessed = 0;
|
|
FLMBOOL bUpdateTracker = FALSE;
|
|
FLMBOOL bRelinquish = FALSE;
|
|
FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE];
|
|
FLMUINT uiKeyLen;
|
|
void * pvTmpPoolMark = m_TempPool.poolMark();
|
|
F_Btree * pbtree = NULL;
|
|
FLMBOOL bNeg;
|
|
FLMUINT uiBytesProcessed;
|
|
F_DOMNode * pNode = NULL;
|
|
|
|
FLM_MILLI_TO_TIMER_UNITS( 500, uiMinTU); // 1/2 second
|
|
FLM_SECS_TO_TIMER_UNITS( 10, uiStatusIntervalTU); // 10 seconds
|
|
uiStartTime = FLM_GET_TIMER();
|
|
|
|
if (RC_BAD( rc = krefCntrlCheck()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pDict->getIndex( uiIxNum, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
flmAssert( !(pIxd->uiFlags & IXD_SUSPENDED));
|
|
|
|
// Get a btree
|
|
|
|
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pDict->getCollection(
|
|
pIxd->uiCollectionNum, &pCollection)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo,
|
|
FALSE, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiKeyLen = sizeof( ucKey);
|
|
if (RC_BAD( rc = flmNumber64ToStorage( ui64StartDocumentId, &uiKeyLen,
|
|
ucKey, FALSE, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if( RC_BAD( rc = pbtree->btLocateEntry(
|
|
ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL)))
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
bHitEnd = TRUE;
|
|
goto Commit_Keys;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Make sure we hit a root node. If not, continue reading until we do
|
|
// or until we hit the end. Root nodes are always linked together in
|
|
// ascending order, so if there is another document, we will find it
|
|
// simply by searching forward from where we are. Then we can follow
|
|
// document links.
|
|
|
|
for (;;)
|
|
{
|
|
if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey,
|
|
&ui64DocumentId, &bNeg, &uiBytesProcessed)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = getNode( pIxd->uiCollectionNum, ui64DocumentId,
|
|
&pNode)))
|
|
{
|
|
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
|
|
// Better be able to find the node at this point!
|
|
|
|
rc = RC_SET( NE_XFLM_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If the node is a root node, we have a document we can
|
|
// process.
|
|
|
|
if (pNode->isRootNode())
|
|
{
|
|
|
|
// This is a root node - has no parent and is not linked
|
|
// into orphan list.
|
|
|
|
break;
|
|
}
|
|
|
|
// Need to go to the next node.
|
|
|
|
if (RC_BAD( rc = pbtree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen)))
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
bHitEnd = TRUE;
|
|
goto Commit_Keys;
|
|
}
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if( RC_BAD( rc = pNode->getNodeId( this, &ui64DocumentId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (ui64DocumentId > ui64EndDocumentId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (RC_BAD( rc = indexDocument( pIxd, pNode)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if there is an indexing callback
|
|
|
|
if (ifpIxClient)
|
|
{
|
|
if (RC_BAD( rc = ifpIxClient->doIndexing( this, uiIxNum,
|
|
pIxd->uiCollectionNum, pNode)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
ui64LastDocumentId = ui64DocumentId;
|
|
ui64DocumentsProcessed++;
|
|
|
|
if (pIndexStatus)
|
|
{
|
|
pIndexStatus->ui64DocumentsProcessed++;
|
|
pIndexStatus->ui64LastDocumentIndexed = ui64LastDocumentId;
|
|
}
|
|
|
|
// Get the current time
|
|
|
|
uiCurrTime = FLM_GET_TIMER();
|
|
|
|
// Break out if someone is waiting for an update transaction.
|
|
|
|
if (pThread)
|
|
{
|
|
if (pThread->getShutdownFlag())
|
|
{
|
|
bRelinquish = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (pDatabaseLockObj->ThreadWaitingLock())
|
|
{
|
|
// See if our minimum run time has elapsed
|
|
|
|
if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU)
|
|
{
|
|
if (ui64DocumentsProcessed < 50)
|
|
{
|
|
// If there are higher priority waiters in the lock queue,
|
|
// we want to relinquish.
|
|
|
|
if (pDatabaseLockObj->haveHigherPriorityWaiter(
|
|
FLM_BACKGROUND_LOCK_PRIORITY))
|
|
{
|
|
bRelinquish = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRelinquish = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Even if no one has requested a lock for a long time, we
|
|
// still want to periodically commit our transaction so
|
|
// we won't lose more than uiMaxCPInterval timer units worth
|
|
// of work if we crash. We will run until we exceed the checkpoint
|
|
// interval and we see that someone (the checkpoint thread) is
|
|
// waiting for the write lock.
|
|
|
|
if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >
|
|
gv_XFlmSysData.uiMaxCPInterval &&
|
|
m_pDatabase->m_pWriteLockObj->ThreadWaitingLock())
|
|
{
|
|
bRelinquish = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >=
|
|
uiStatusIntervalTU)
|
|
{
|
|
uiLastStatusTime = uiCurrTime;
|
|
if( ifpIxStatus)
|
|
{
|
|
if( RC_BAD( rc = ifpIxStatus->reportIndex( ui64LastDocumentId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Send indexing completed event notification
|
|
|
|
if( gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList)
|
|
{
|
|
flmDoEventCallback( XFLM_EVENT_UPDATES,
|
|
XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(),
|
|
0, uiIxNum, ui64LastDocumentId,
|
|
NE_XFLM_OK);
|
|
}
|
|
|
|
// Log a progress message
|
|
|
|
flmLogIndexingProgress( uiIxNum, ui64LastDocumentId);
|
|
}
|
|
|
|
// Need to go to the next document.
|
|
|
|
if (RC_BAD( rc = pNode->getNextDocument( this, (IF_DOMNode **)&pNode)))
|
|
{
|
|
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
bHitEnd = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Commit_Keys:
|
|
|
|
if (RC_BAD( rc = keysCommit( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If at the end, change trans ID to the current transaction.
|
|
|
|
if (bHitEnd)
|
|
{
|
|
if (RC_BAD( rc = setIxStateInfo( uiIxNum, ~((FLMUINT64)0), 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// setIxStateInfo may have changed to a new dictionary, so pIxd is no
|
|
// good after this point
|
|
|
|
pIxd = NULL;
|
|
}
|
|
else if (ui64DocumentsProcessed || bUpdateTracker)
|
|
{
|
|
if (RC_BAD( rc = setIxStateInfo( uiIxNum, ui64LastDocumentId,
|
|
IXD_OFFLINE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// setIxStateInfo may have changed to a new dictionary, so pIxd is no
|
|
// good after this point
|
|
|
|
pIxd = NULL;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// We want to make one last call if we are in the foreground or if
|
|
// we actually did some indexing.
|
|
|
|
if (gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList)
|
|
{
|
|
flmDoEventCallback( XFLM_EVENT_UPDATES,
|
|
XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(),
|
|
0, uiIxNum,
|
|
(FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId),
|
|
NE_XFLM_OK);
|
|
}
|
|
|
|
flmLogIndexingProgress( uiIxNum,
|
|
(FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId));
|
|
|
|
if (ifpIxStatus)
|
|
{
|
|
(void) ifpIxStatus->reportIndex( ui64LastDocumentId);
|
|
}
|
|
|
|
if (pbHitEnd)
|
|
{
|
|
*pbHitEnd = bHitEnd;
|
|
}
|
|
|
|
krefCntrlFree();
|
|
m_TempPool.poolReset( pvTmpPoolMark);
|
|
|
|
if (pbtree)
|
|
{
|
|
gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree);
|
|
}
|
|
|
|
if (pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Set information in the tracker record for the index.
|
|
****************************************************************************/
|
|
RCODE F_Db::setIxStateInfo(
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT64 ui64LastDocIndexed,
|
|
FLMUINT uiState)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
IXD_FIXUP * pIxdFixup;
|
|
IXD * pIxd;
|
|
F_DOMNode * pAttr = NULL;
|
|
F_DOMNode * pElement = NULL;
|
|
FLMBOOL bMustAbortOnError = FALSE;
|
|
|
|
// Get the IXD - even if the index is offline.
|
|
|
|
if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if this index is in our fixup list.
|
|
|
|
pIxdFixup = m_pIxdFixups;
|
|
while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum)
|
|
{
|
|
pIxdFixup = pIxdFixup->pNext;
|
|
}
|
|
|
|
if (!pIxdFixup)
|
|
{
|
|
if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( IXD_FIXUP), &pIxdFixup)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pIxdFixup->pNext = m_pIxdFixups;
|
|
m_pIxdFixups = pIxdFixup;
|
|
pIxdFixup->uiIndexNum = uiIndexNum;
|
|
pIxdFixup->ui64LastDocIndexed = pIxd->ui64LastDocIndexed;
|
|
}
|
|
|
|
bMustAbortOnError = TRUE;
|
|
|
|
// Update the last node indexed, if it changed.
|
|
|
|
if (pIxdFixup->ui64LastDocIndexed != ui64LastDocIndexed)
|
|
{
|
|
pIxdFixup->ui64LastDocIndexed = ui64LastDocIndexed;
|
|
|
|
// First, retrieve the root element of the index definition.
|
|
|
|
if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId,
|
|
(F_DOMNode **)&pElement)))
|
|
{
|
|
if( rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// No need to create a new node if we are just setting it
|
|
// to the default value - see fdict.cpp, getIndexDef for where
|
|
// the default value gets set.
|
|
|
|
if (ui64LastDocIndexed != ~((FLMUINT64)0))
|
|
{
|
|
|
|
// Create a new dictionary - so that we can set the
|
|
// ui64LastDocIndexedNodeId on the IXD. If the transaction
|
|
// aborts, the whole dictionary will go away.
|
|
|
|
if (!(m_uiFlags & FDB_UPDATED_DICTIONARY))
|
|
{
|
|
if (RC_BAD( rc = dictClone()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get a pointer to the new IXD
|
|
|
|
if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Create a new attribute node on the index definition to hold
|
|
// the last node indexed value.
|
|
|
|
if (RC_BAD( rc = pElement->createAttribute( this,
|
|
ATTR_LAST_DOC_INDEXED_TAG,
|
|
(IF_DOMNode **)&pAttr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = pElement->getAttribute( this,
|
|
ATTR_LAST_DOC_INDEXED_TAG, (IF_DOMNode **)&pAttr)))
|
|
{
|
|
if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
}
|
|
|
|
if( pAttr)
|
|
{
|
|
if (RC_BAD( rc = pAttr->setUINT64( this, ui64LastDocIndexed)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If IXD_SUSPENDED is set, then IXD_OFFLINE must also be set.
|
|
// There are places in the code that only check for IXD_OFFLINE
|
|
// that don't care if the index is also suspended.
|
|
|
|
if (uiState & IXD_SUSPENDED)
|
|
{
|
|
uiState = IXD_SUSPENDED | IXD_OFFLINE;
|
|
}
|
|
else if (uiState & IXD_OFFLINE)
|
|
{
|
|
uiState = IXD_OFFLINE;
|
|
}
|
|
else
|
|
{
|
|
uiState = 0;
|
|
}
|
|
|
|
// See if we need to change state.
|
|
|
|
if ((pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE)) != uiState)
|
|
{
|
|
const char * pszStateStr;
|
|
|
|
if (uiState & IXD_SUSPENDED)
|
|
{
|
|
pszStateStr = XFLM_INDEX_SUSPENDED_STR;
|
|
}
|
|
else if (uiState & IXD_OFFLINE)
|
|
{
|
|
pszStateStr = XFLM_INDEX_OFFLINE_STR;
|
|
}
|
|
else
|
|
{
|
|
pszStateStr = XFLM_INDEX_ONLINE_STR;
|
|
}
|
|
|
|
// At this point we know we need to change the state. That means we need
|
|
// to create a new dictionary, if we have not already done so.
|
|
|
|
if (!(m_uiFlags & FDB_UPDATED_DICTIONARY))
|
|
{
|
|
if (RC_BAD( rc = dictClone()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get a pointer to the new IXD
|
|
|
|
if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// pElement may have been fetched above. Don't need to get it
|
|
// here if that is the case.
|
|
|
|
if (!pElement)
|
|
{
|
|
|
|
// First, retrieve the root element of the index definition.
|
|
|
|
if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId,
|
|
(F_DOMNode **)&pElement)))
|
|
{
|
|
if( rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Create a new attribute node on the index definition to hold
|
|
// the last node indexed value.
|
|
|
|
if (RC_BAD( rc = pElement->createAttribute( this,
|
|
ATTR_STATE_TAG,
|
|
(IF_DOMNode **)&pAttr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// May need to unfreeze the state to change it.
|
|
|
|
if( RC_BAD( rc = pAttr->removeModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pszStateStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pAttr->addModeFlags( this,
|
|
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Put the state into the IXD.
|
|
|
|
pIxd->uiFlags = (pIxd->uiFlags & (~(IXD_SUSPENDED | IXD_OFFLINE))) |
|
|
uiState;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pAttr)
|
|
{
|
|
pAttr->Release();
|
|
}
|
|
|
|
if (pElement)
|
|
{
|
|
pElement->Release();
|
|
}
|
|
|
|
if( RC_BAD( rc) && bMustAbortOnError)
|
|
{
|
|
setMustAbortTrans( rc);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: See if any IXD structures need indexing in the background.
|
|
****************************************************************************/
|
|
RCODE F_Db::startBackgroundIndexing( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBOOL bStartedTrans = FALSE;
|
|
FLMUINT uiIndexNum;
|
|
IXD * pIxd;
|
|
|
|
if (RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_eTransType != XFLM_NO_TRANS)
|
|
{
|
|
if (!okToCommitTrans())
|
|
{
|
|
rc = RC_SET( NE_XFLM_ABORT_TRANS);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Need to have at least a read transaction going.
|
|
|
|
if (RC_BAD( rc = beginTrans( XFLM_READ_TRANS)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bStartedTrans = TRUE;
|
|
}
|
|
|
|
if (m_pDict->getIndexCount( FALSE))
|
|
{
|
|
uiIndexNum = 0;
|
|
for (;;)
|
|
{
|
|
if ((pIxd = m_pDict->getNextIndex( uiIndexNum, FALSE)) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
uiIndexNum = pIxd->uiIndexNum;
|
|
|
|
// Restart any indexes that are off-line but not suspended
|
|
|
|
if ((pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) == IXD_OFFLINE)
|
|
{
|
|
flmAssert( flmBackgroundIndexGet( m_pDatabase,
|
|
uiIndexNum, FALSE) == NULL);
|
|
|
|
if (RC_BAD( rc = startIndexBuild( uiIndexNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (bStartedTrans)
|
|
{
|
|
(void)abortTrans();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Check and set the next dictionary number for a specific dictionary type.
|
|
****************************************************************************/
|
|
RCODE F_Db::setNextDictNum(
|
|
FLMUINT uiDictType,
|
|
FLMUINT uiDictNumber
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
// Make sure an update transaction is active
|
|
|
|
if (m_eTransType == XFLM_NO_TRANS)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_eTransType == XFLM_READ_TRANS)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the transaction needs to be aborted
|
|
|
|
if (RC_BAD( rc = m_AbortRc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// The number must be greater than 1
|
|
|
|
if (uiDictNumber < 2)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set the next dictionary number
|
|
|
|
if (RC_BAD( rc = m_pDict->setNextDictNum( this, uiDictType, uiDictNumber)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc:
|
|
*****************************************************************************/
|
|
RCODE F_Database::startMaintThread( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
char szThreadName[ F_PATH_MAX_SIZE];
|
|
char szBaseName[ 32];
|
|
|
|
flmAssert( !m_pMaintThrd);
|
|
flmAssert( m_hMaintSem == F_SEM_NULL);
|
|
|
|
// Generate the thread name
|
|
|
|
if( RC_BAD( rc = gv_pFileSystem->pathReduce(
|
|
m_pszDbPath, szThreadName, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_sprintf( (char *)szThreadName, "Maintenance (%s)", (char *)szBaseName);
|
|
|
|
// Create the maintenance semaphore
|
|
|
|
if( RC_BAD( rc = f_semCreate( &m_hMaintSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Start the thread.
|
|
|
|
if( RC_BAD( rc = f_threadCreate( &m_pMaintThrd,
|
|
F_Database::maintenanceThread, szThreadName,
|
|
FLM_DEFAULT_THREAD_GROUP, 0, this, NULL, 32000)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Signal the thread to check for any queued work
|
|
|
|
f_semSignal( m_hMaintSem);
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
if( m_hMaintSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &m_hMaintSem);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_Db::beginBackgroundTrans(
|
|
F_Thread * pThread)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
RetryLock:
|
|
|
|
// Obtain the file lock
|
|
|
|
flmAssert( !(m_uiFlags & FDB_HAS_FILE_LOCK));
|
|
|
|
if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Lock( this, m_hWaitSem,
|
|
TRUE, FALSE, TRUE, XFLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY,
|
|
m_pDbStats)))
|
|
{
|
|
if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT)
|
|
{
|
|
// This would only happen if we were signaled to shut down.
|
|
// So, it's ok to exit
|
|
|
|
flmAssert( pThread->getShutdownFlag());
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// The lock needs to be marked as implicit so that commitTrans
|
|
// will unlock the database and allow the next update transaction to
|
|
// begin before all writes are complete.
|
|
|
|
m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
|
|
|
|
// If there are higher priority waiters in the lock queue,
|
|
// we want to relinquish.
|
|
|
|
if( m_pDatabase->m_pDatabaseLockObj->haveHigherPriorityWaiter(
|
|
FLM_BACKGROUND_LOCK_PRIORITY))
|
|
{
|
|
if( pThread->getShutdownFlag())
|
|
{
|
|
goto Exit;
|
|
}
|
|
if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
|
|
goto RetryLock;
|
|
}
|
|
|
|
// If we are shutting down, relinquish and exit.
|
|
|
|
if( pThread->getShutdownFlag())
|
|
{
|
|
rc = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT);
|
|
goto Exit;
|
|
}
|
|
|
|
// Start an update transaction
|
|
|
|
if( RC_BAD( rc = beginTrans(
|
|
XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE)))
|
|
{
|
|
if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT)
|
|
{
|
|
// This would only happen if we were signaled to shut down.
|
|
// So, it's ok to exit
|
|
|
|
flmAssert( pThread->getShutdownFlag());
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
if( m_uiFlags & FDB_HAS_FILE_LOCK)
|
|
{
|
|
(void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this);
|
|
m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Thread that will build an index in the background.
|
|
Caller will create a pDb to use. This pDb must be
|
|
freed at the conclusion of the routine.
|
|
****************************************************************************/
|
|
RCODE F_Database::maintenanceThread(
|
|
F_Thread * pThread)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_Database * pDatabase = (F_Database *)pThread->getParm1();
|
|
F_Db * pDb;
|
|
F_DOMNode * pDoc;
|
|
F_DOMNode * pNextDoc;
|
|
FLMUINT64 ui64DocId;
|
|
FLMUINT64 ui64TmpTransId;
|
|
FLMUINT64 ui64SweepTransId;
|
|
FLMUINT uiNameId;
|
|
FLMBOOL bStartedTrans;
|
|
FLMBOOL bShutdown;
|
|
F_DbSystem * pDbSystem = NULL;
|
|
|
|
Retry:
|
|
|
|
rc = NE_XFLM_OK;
|
|
pDb = NULL;
|
|
pDoc = NULL;
|
|
pNextDoc = NULL;
|
|
bStartedTrans = FALSE;
|
|
bShutdown = FALSE;
|
|
|
|
if( (pDbSystem = f_new F_DbSystem) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING);
|
|
|
|
if( RC_BAD( rc = pDbSystem->internalDbOpen( pDatabase, &pDb)))
|
|
{
|
|
// If the file is being closed, this is not an error.
|
|
|
|
if( pDatabase->getFlags() & DBF_BEING_CLOSED)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
bShutdown = TRUE;
|
|
}
|
|
goto Exit;
|
|
}
|
|
pDbSystem->Release();
|
|
pDbSystem = NULL;
|
|
|
|
for( ;;)
|
|
{
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
|
|
ui64DocId = 0;
|
|
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bStartedTrans = TRUE;
|
|
|
|
if( RC_BAD( rc = pDb->getDocument(
|
|
XFLM_MAINT_COLLECTION, XFLM_INCL, ui64DocId, (IF_DOMNode **)&pDoc)))
|
|
{
|
|
if( rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
RC_UNEXPECTED_ASSERT( rc);
|
|
goto Exit;
|
|
}
|
|
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
|
|
ui64DocId = pDoc->getDocumentId();
|
|
|
|
if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiNameId == ELM_DELETE_TAG)
|
|
{
|
|
if( RC_BAD( rc = pDb->maintBlockChainFree(
|
|
ui64DocId, 25, 0, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bStartedTrans = FALSE;
|
|
if( RC_BAD( rc = pDb->commitTrans( 0, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if( uiNameId == ELM_SWEEP_TAG)
|
|
{
|
|
ui64SweepTransId = pDb->getTransID();
|
|
pDb->abortTrans();
|
|
bStartedTrans = FALSE;
|
|
|
|
if( RC_BAD( rc = pDb->sweep( pThread)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Delete the sweep documents from the tracker
|
|
|
|
if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bStartedTrans = TRUE;
|
|
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = pDoc->getNextDocument( pDb,
|
|
(IF_DOMNode **)&pNextDoc)))
|
|
{
|
|
if( rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
RC_UNEXPECTED_ASSERT( rc);
|
|
goto Exit;
|
|
}
|
|
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiNameId == ELM_SWEEP_TAG)
|
|
{
|
|
if( RC_BAD( rc = pDoc->getAttributeValueUINT64(
|
|
pDb, ATTR_TRANSACTION_TAG, &ui64TmpTransId, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( ui64TmpTransId > ui64SweepTransId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDoc->removeModeFlags(
|
|
pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDoc->deleteNode( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pDoc->Release();
|
|
|
|
// Use the reference on pNextDoc and set it to NULL so that
|
|
// it doesn't get released.
|
|
|
|
pDoc = pNextDoc;
|
|
pNextDoc = NULL;
|
|
}
|
|
|
|
bStartedTrans = FALSE;
|
|
if( RC_BAD( rc = pDb->commitTrans( 0, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flmAssert( bStartedTrans);
|
|
pDb->abortTrans();
|
|
bStartedTrans = FALSE;
|
|
}
|
|
|
|
ui64DocId++;
|
|
}
|
|
|
|
if( bStartedTrans)
|
|
{
|
|
pDb->abortTrans();
|
|
bStartedTrans = FALSE;
|
|
}
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);
|
|
f_semWait( pDatabase->m_hMaintSem, F_SEM_WAITFOREVER);
|
|
|
|
if( pThread->getShutdownFlag())
|
|
{
|
|
bShutdown = TRUE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);
|
|
|
|
if( pDbSystem)
|
|
{
|
|
pDbSystem->Release();
|
|
}
|
|
|
|
if( pDoc)
|
|
{
|
|
pDoc->Release();
|
|
}
|
|
|
|
if( pNextDoc)
|
|
{
|
|
pNextDoc->Release();
|
|
}
|
|
|
|
if( bStartedTrans)
|
|
{
|
|
pDb->abortTrans();
|
|
}
|
|
|
|
if( pDb)
|
|
{
|
|
pDb->Release();
|
|
pDb = NULL;
|
|
}
|
|
|
|
if( !bShutdown)
|
|
{
|
|
flmAssert( RC_BAD( rc));
|
|
f_sleep( 250);
|
|
f_semSignal( pDatabase->m_hMaintSem);
|
|
goto Retry;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_Db::maintBlockChainFree(
|
|
FLMUINT64 ui64MaintDocID,
|
|
FLMUINT uiBlocksToFree,
|
|
FLMUINT uiExpectedEndAddr,
|
|
FLMUINT * puiBlocksFreed)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiTmp;
|
|
FLMUINT uiBlocksFreed = 0;
|
|
FLMUINT uiStartAddr = 0;
|
|
FLMUINT uiEndAddr = 0;
|
|
F_DOMNode * pDoc = NULL;
|
|
F_DOMNode * pChainNode = NULL;
|
|
F_DOMNode * pAddrNode = NULL;
|
|
FLMUINT uiRflToken = 0;
|
|
|
|
// Make sure an update transaction is going and that a
|
|
// non-zero number of blocks was specified
|
|
|
|
if( getTransType() != XFLM_UPDATE_TRANS || !uiBlocksToFree)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
// Retrieve the maintenance document
|
|
|
|
if( RC_BAD( rc = getNode( XFLM_MAINT_COLLECTION,
|
|
ui64MaintDocID, XFLM_EXACT, &pDoc)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pDatabase->m_pRfl->disableLogging( &uiRflToken);
|
|
|
|
while( uiBlocksFreed < uiBlocksToFree)
|
|
{
|
|
if( RC_BAD( rc = pDoc->getChildElement(
|
|
this, ELM_BLOCK_CHAIN_TAG, (IF_DOMNode **)&pChainNode)))
|
|
{
|
|
if( rc != NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDoc->removeModeFlags(
|
|
this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDoc->deleteNode( this)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if( RC_BAD( rc = pChainNode->getAttributeValueUINT(
|
|
this, ATTR_ADDRESS_TAG, &uiStartAddr, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( !uiStartAddr)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = btFreeBlockChain(
|
|
this, NULL, uiStartAddr, uiBlocksToFree - uiBlocksFreed,
|
|
&uiTmp, &uiEndAddr, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiBlocksFreed += uiTmp;
|
|
flmAssert( uiBlocksFreed <= uiBlocksToFree);
|
|
|
|
if( RC_BAD( rc = pChainNode->removeModeFlags(
|
|
this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( !uiEndAddr)
|
|
{
|
|
if( RC_BAD( rc = pChainNode->deleteNode( this)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = pChainNode->getAttribute(
|
|
this, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pAddrNode->removeModeFlags(
|
|
this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pAddrNode->setUINT( this, uiEndAddr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pAddrNode->addModeFlags(
|
|
this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pChainNode->addModeFlags(
|
|
this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = documentDone(
|
|
XFLM_MAINT_COLLECTION, ui64MaintDocID)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( uiExpectedEndAddr)
|
|
{
|
|
if( uiBlocksToFree != uiBlocksFreed ||
|
|
uiEndAddr != uiExpectedEndAddr)
|
|
{
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ( uiRflToken)
|
|
{
|
|
m_pDatabase->m_pRfl->enableLogging( &uiRflToken);
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pDatabase->m_pRfl->logBlockChainFree(
|
|
this, ui64MaintDocID, uiStartAddr, uiEndAddr, uiBlocksFreed)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( puiBlocksFreed)
|
|
{
|
|
*puiBlocksFreed = uiBlocksFreed;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( uiRflToken)
|
|
{
|
|
m_pDatabase->m_pRfl->enableLogging( &uiRflToken);
|
|
}
|
|
|
|
if( pChainNode)
|
|
{
|
|
pChainNode->Release();
|
|
}
|
|
|
|
if( pAddrNode)
|
|
{
|
|
pAddrNode->Release();
|
|
}
|
|
|
|
if( pDoc)
|
|
{
|
|
pDoc->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|