/* Copyright (C) 2014 InfiniDB, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /****************************************************************************************** * $Id: we_indextree.cpp 4450 2013-01-21 14:13:24Z rdempsey $ * ******************************************************************************************/ /** @file */ #include #include #define WRITEENGINEINDEXTREE_DLLEXPORT #include "we_indextree.h" #undef WRITEENGINEINDEXTREE_DLLEXPORT namespace WriteEngine { /** * Constructor */ IndexTree::IndexTree() : m_useFreeMgr(true), m_useListMgr(true), m_useMultiCol(false), m_useMultiRid(false), m_assignFbo(0) { clearBlock(&m_rootBlock); } /** * Default Destructor */ IndexTree::~IndexTree() { } /* const int IndexTree::getIndexTreeBitTestEntry( uint64_t entry, short* entryType, int* bitTest, int* group, int32_t* treePointer ) { *treePointer = entry & IDX_PTR_MASK; entry = entry >> IDX_PTR_SIZE + 2; // skip one spare bit and bit-compare bit *group = entry & THREE_BIT_MASK; entry = entry >> IDX_GROUP_SIZE; *bitTest = entry & TEN_BIT_MASK; *entryType = entry >> IDX_BITTEST_SIZE; *entryType = entry & THREE_BIT_MASK; return NO_ERROR; } const void IndexTree::setIndexTreeBitTestEntry( uint64_t* entry, short entryType, int bitTest, int group, int32_t treePointer ) { memset( entry, 0, ROW_PER_BYTE ); *entry = treePointer | group << ( IDX_PTR_SIZE + 2 ); *entry = *entry | bitTest << ( IDX_PTR_SIZE + 2 + IDX_GROUP_SIZE ); *entry = *entry | entryType << ( IDX_PTR_SIZE + 2 + IDX_GROUP_SIZE + IDX_TYPE_SIZE ); return NO_ERROR; } */ /*********************************************************** * DESCRIPTION: * Clear the tree * PARAMETERS: * myTree - tree pointer * RETURN: * none ***********************************************************/ void IndexTree::clearTree(IdxTree* myTree) { myTree->width = myTree->key = myTree->rid = myTree->maxLevel = 0; for (int i = 0; i < IDX_MAX_TREE_LEVEL; i++) clearTreeNode(&myTree->node[i]); } /*********************************************************** * DESCRIPTION: * Clear the tree node * PARAMETERS: * myNode - node pointer * RETURN: * none ***********************************************************/ void IndexTree::clearTreeNode(IdxTreeNode* myNode) { myNode->level = 0; myNode->allocCount = 0; myNode->useCount = 0; // myNode->group = 0; myNode->used = false; setBlankEntry(&myNode->next); setBlankEntry(&myNode->current); } /*********************************************************** * DESCRIPTION: * Assign segment from free manager * PARAMETERS: * segmentType - group type * assignPtr - the assigned ptr * no - internal debug use flag * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::assignSegment(int segmentType, IdxEmptyListEntry* assignPtr, int no) { int rc = NO_ERROR; m_rootBlock.dirty = true; if (m_useFreeMgr) { if (isDebug(DEBUG_3)) { printf("\n++++++ Before Assign"); printMemSubBlock(&m_rootBlock, 0, true); } rc = m_freeMgr.assignSegment(/*m_pTreeFile*/ m_cbTree, &m_rootBlock, TREE, (IdxTreeGroupType)segmentType, assignPtr /*, TREE */); if (isDebug(DEBUG_3)) { printf("\nAssign the pointer, entry segment=%d fbo=%2d sbid=%2d entry=%2d", segmentType, (int)assignPtr->fbo, (int)assignPtr->sbid, (int)assignPtr->entry); printMemSubBlock(&m_rootBlock, 0, true); } } else { assignPtr->fbo = 1 + m_assignFbo; assignPtr->sbid = 3 + no; assignPtr->entry = 4; } return rc; } /*********************************************************** * DESCRIPTION: * Build a complete empty tree branch * PARAMETERS: * key - key value * width - key width * rid - row id * rootTestbitVal - test bit test value at root level * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::buildEmptyIndexTreeBranch(const uint64_t key, const int width, const RID rid, const int rootTestbitVal) { int rc; IdxBitmapPointerEntry bitmapEntry = {0}; rc = buildEmptyTreePart(key, width, rid, 1); // set the root level bitmapPointerMap bitmapEntry.type = BITMAP_PTR; // BIT_TEST; bitmapEntry.fbo = m_tree.node[0].next.fbo; bitmapEntry.sbid = m_tree.node[0].next.sbid; bitmapEntry.entry = m_tree.node[0].next.entry; setSubBlockEntry(m_rootBlock.data, IDX_BITMAP_SUBBLOCK_NO, rootTestbitVal, MAX_COLUMN_BOUNDARY, &bitmapEntry); m_rootBlock.dirty = true; return rc; } /*********************************************************** * DESCRIPTION: * Build exist index tree branch * PARAMETERS: * key - key value * width - key width * rid - row id * rootTestbitVal - test bit at root level * bitmapEntry - current bitmap entry * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::buildExistIndexTreeBranch(const uint64_t key, const int width, const RID rid, const int rootTestbitVal, IdxBitmapPointerEntry bitmapEntry) { int rc = NO_ERROR, loopCount, testbitVal, i, j, allocCount, realCount, matchPos, moveCount, parentLevel = 0, curLevel, curOffset = 0; bool bSuccess; IdxEmptyListEntry assignPtrEntry, releasePtrEntry; IdxBitTestEntry bittestEntry, matchBitTestEntry, parentBitTestEntry, curEntry; DataBlock curBlock, parentBlock; bool bAddFlag = false, bDone = false, bFound, bExitOuterLoop, bExitInnerLoop, entryMap[ENTRY_PER_SUBBLOCK]; bExitOuterLoop = false; loopCount = m_tree.maxLevel; for (i = 1; !bExitOuterLoop && i < loopCount; i++) { // load the block rc = readSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; if (i == 1 && isAddrPtrEmpty(&bittestEntry, BIT_TEST)) return ERR_STRUCT_EMPTY; rc = getTreeNodeInfo(&curBlock, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, width, (IdxTreeGroupType)bittestEntry.group, &allocCount, &realCount, entryMap); if (rc != NO_ERROR) return rc; matchBitTestEntry = bittestEntry; // assign to the value of the first entry bSuccess = getTestbitValue(key, width, i, &testbitVal); setBittestEntry(&curEntry, testbitVal, bittestEntry.group, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry); setTreeNode(&m_tree.node[i], i, allocCount, realCount, 0, bittestEntry, curEntry); matchBitTestEntry.bitTest = testbitVal; bFound = getTreeMatchEntry(&curBlock, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, width, allocCount, entryMap, &matchPos, &matchBitTestEntry); if (!bFound) // this testbit not exist at the current level { bExitOuterLoop = true; // tell to exit the outer loop bAddFlag = true; if (allocCount < realCount + 1) // we have enough space to take care of the extra one { // we don't have space to take care of the extra one, have to reassign to a big block if (bittestEntry.group >= ENTRY_32) // it's impossible this condition holds true return ERR_IDX_TREE_INVALID_GRP; m_tree.node[i].current.group++; rc = assignSegment(m_tree.node[i].current.group, &assignPtrEntry, i); if (rc != NO_ERROR) return rc; m_tree.node[i].allocCount = 0x1 << m_tree.node[i].current.group; if (isDebug(DEBUG_3)) { printf("\nEntry starting from %d:%d:%d (type %d ) will move to %d:%d:%d (type %d)", (int)m_tree.node[parentLevel].next.fbo, (int)m_tree.node[parentLevel].next.sbid, (int)m_tree.node[parentLevel].next.entry, (int)(m_tree.node[i].current.group - 1), (int)assignPtrEntry.fbo, (int)assignPtrEntry.sbid, (int)assignPtrEntry.entry, (int)m_tree.node[i].current.group); printf("\nNew space capacity is %d", (int)m_tree.node[i].allocCount); printf("\nBefore the move"); printSubBlock(m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid); printSubBlock(assignPtrEntry.fbo, assignPtrEntry.sbid); } rc = moveEntry(m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, width, assignPtrEntry.fbo, assignPtrEntry.sbid, assignPtrEntry.entry, m_tree.node[i].current.group, allocCount, entryMap, &moveCount, m_tree.node[i].allocCount); if (rc != NO_ERROR) return rc; if (isDebug(DEBUG_3)) { printf("\nAfter the move"); printSubBlock(m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid); printSubBlock(assignPtrEntry.fbo, assignPtrEntry.sbid); } if (moveCount != realCount) return ERR_IDX_TREE_MOVE_ENTRY; // set release entry setEmptyListEntry(&releasePtrEntry, bittestEntry.group, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry); if (i == 1) // handle bitmap parent { bitmapEntry.fbo = m_tree.node[0].next.fbo = m_tree.node[i].current.fbo = assignPtrEntry.fbo; bitmapEntry.sbid = m_tree.node[0].next.sbid = m_tree.node[i].current.sbid = assignPtrEntry.sbid; bitmapEntry.entry = m_tree.node[0].next.entry = m_tree.node[i].current.entry = assignPtrEntry.entry; setSubBlockEntry(m_rootBlock.data, IDX_BITMAP_SUBBLOCK_NO, rootTestbitVal, MAX_COLUMN_BOUNDARY, &bitmapEntry); } else // handle parent for the rest of levels in the tree { rc = readSubBlockEntry(m_cbTree, &parentBlock, m_tree.node[parentLevel].current.fbo, m_tree.node[parentLevel].current.sbid, m_tree.node[parentLevel].current.entry, MAX_COLUMN_BOUNDARY, &parentBitTestEntry); if (rc != NO_ERROR) return rc; parentBitTestEntry.fbo = m_tree.node[parentLevel].next.fbo = m_tree.node[i].current.fbo = assignPtrEntry.fbo; parentBitTestEntry.sbid = m_tree.node[parentLevel].next.sbid = m_tree.node[i].current.sbid = assignPtrEntry.sbid; parentBitTestEntry.entry = m_tree.node[parentLevel].next.entry = m_tree.node[i].current.entry = assignPtrEntry.entry; rc = writeSubBlockEntry(m_cbTree, &parentBlock, m_tree.node[parentLevel].current.fbo, m_tree.node[parentLevel].current.sbid, m_tree.node[parentLevel].current.entry, MAX_COLUMN_BOUNDARY, &parentBitTestEntry); if (rc != NO_ERROR) return rc; } // here's the work to release the ptr rc = releaseSegment(bittestEntry.group, &releasePtrEntry); if (rc != NO_ERROR) return rc; } // end of if( allocCount >= // take care of rest of empty part rc = readSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; m_tree.node[i].current.bitTest = testbitVal; matchBitTestEntry.group = m_tree.node[i].current.group; m_tree.node[i].allocCount = 0x1 << m_tree.node[i].current.group; bExitInnerLoop = false; for (j = 0; !bExitInnerLoop && j < m_tree.node[i].allocCount; j++) if (!entryMap[j]) // here's the empty spot { if (m_tree.maxLevel > 2 && i != loopCount - 1) { rc = buildEmptyTreePart(key, width, rid, i + 1, 0); if (rc != NO_ERROR) return rc; bDone = true; } // check out of bound rc = readSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry + j, MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; matchBitTestEntry.fbo = m_tree.node[i + 1].current.fbo; matchBitTestEntry.sbid = m_tree.node[i + 1].current.sbid; matchBitTestEntry.entry = m_tree.node[i + 1].current.entry; rc = writeSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry + j, MAX_COLUMN_BOUNDARY, &matchBitTestEntry); if (rc != NO_ERROR) return rc; m_tree.node[i].useCount++; m_tree.node[i].current.entry += j; entryMap[j] = true; curOffset = j; bExitInnerLoop = true; } // end of if( !entryMap[j] ) } // end of if( !bFound else { m_tree.node[i].current.entry += matchPos; m_tree.node[parentLevel].next.entry += matchPos; } m_tree.node[i].next = matchBitTestEntry; parentLevel++; } // end of for( i = 1; curLevel = m_tree.maxLevel - 1; if (!bDone) { rc = updateListFile(key, width, rid, curLevel, m_tree.node[curLevel].current.group, m_tree.node[curLevel].allocCount, m_tree.node[curLevel].useCount, curOffset, bAddFlag); } return rc; } /*********************************************************** * DESCRIPTION: * Build a a part of empty tree branch * PARAMETERS: * key - key value * width - key width * rid - row id * startLevel - tree level * RETURN: * NO_ERROR if success * error no if fail * retBitTestEntry - return address pointer ***********************************************************/ const int IndexTree::buildEmptyTreePart(const uint64_t key, const int width, const RID rid, const int startLevel, const int offset) { int rc, loopCount, testbitVal, i, parentLevel; bool bSuccess; IdxEmptyListEntry assignPtrEntry, childPtrEntry; //, listEntry; IdxBitTestEntry bittestEntry, curEntry; DataBlock curBlock; if (startLevel <= 0 || m_tree.maxLevel < 2) // the start level must >= 1 and maxLevel >= 2 return ERR_IDX_TREE_INVALID_LEVEL; loopCount = (m_tree.maxLevel - 1) > 1 ? m_tree.maxLevel - 1 : 0; //( width/5 ) - 1; rc = assignSegment(ENTRY_1, &assignPtrEntry, 0); if (rc != NO_ERROR) return rc; if (isAddrPtrEmpty(&assignPtrEntry, EMPTY_LIST)) return ERR_STRUCT_EMPTY; parentLevel = startLevel - 1; // assuming the parent take care of group, bit test value, and type m_tree.node[parentLevel].next.fbo = assignPtrEntry.fbo; m_tree.node[parentLevel].next.sbid = assignPtrEntry.sbid; m_tree.node[parentLevel].next.entry = assignPtrEntry.entry; // assign bit test for rest of levels for (i = startLevel; i < loopCount; i++) { // assign another one for child rc = assignSegment(ENTRY_1, &childPtrEntry, i); if (rc != NO_ERROR) return rc; rc = readSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, /*width*/ MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; bSuccess = getTestbitValue(key, width, i, &testbitVal); if (!bSuccess) return ERR_IDX_TREE_INVALID_LEVEL; setBittestEntry(&bittestEntry, testbitVal, ENTRY_1, childPtrEntry.fbo, childPtrEntry.sbid, childPtrEntry.entry); setSubBlockEntry(&curBlock, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, MAX_COLUMN_BOUNDARY, &bittestEntry); writeDBFile(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo); setBittestEntry(&curEntry, testbitVal, ENTRY_1, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry); setTreeNode(&m_tree.node[i], i, 1, 1, 0, bittestEntry, curEntry); parentLevel++; } // assign the bit test for the last level // load the last piece rc = updateListFile(key, width, rid, i, ENTRY_1, 1, 1, offset, true); return rc; } /*********************************************************** * DESCRIPTION: * Close index files * PARAMETERS: * none * RETURN: * none ***********************************************************/ void IndexTree::closeIndex() { if (m_rootBlock.dirty) { uint64_t lbid0 = 0; #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID BRMWrapper::getInstance()->getBrmInfo(m_cbTree.file.oid, 0, lbid0); #endif writeDBFile(m_cbTree, m_rootBlock.data, lbid0); } if (Cache::getUseCache()) flushCache(); m_fileopTree.closeFile(m_cbTree.file.pFile); m_fileopList.closeFile(m_cbList.file.pFile); m_cbTree.file.pFile = NULL; m_cbList.file.pFile = NULL; clear(); } /*********************************************************** * DESCRIPTION: * Create index related files * PARAMETERS: * treeFid - the index tree file id * listFid - the index list file id * useFreeMgrFlag - internal use flag * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::createIndex(const FID treeFid, const FID listFid, const bool useFreeMgrFlag) { #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID int allocSize; // init // clear(); RETURN_ON_ERROR(createFile(treeFid, DEFAULT_TOTAL_BLOCK /* * 10*/, allocSize)); RETURN_ON_ERROR(createFile(listFid, DEFAULT_TOTAL_BLOCK /* * 10*/, allocSize)); #endif // load index files // RETURN_ON_ERROR( openIndex( treeFid, listFid ) ); // rc = initIndex( treeFid, listFid ); // closeIndex(); return initIndex(treeFid, listFid); } /*********************************************************** * DESCRIPTION: * Delete a value from the tree * PARAMETERS: * key - key value * width - key width * rid - row id * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::deleteIndex(const uint64_t key, const int width, const RID rid) { IdxEmptyListEntry listHdrAddr; return processIndex(key, width, rid, listHdrAddr); } /*********************************************************** * DESCRIPTION: * Get test bit values * PARAMETERS: * key - index key * width - key width * curTestNo - test bit iteration no * RETURN: * True if success, otherwise if out of bound * bittestVal - test bit value ***********************************************************/ const bool IndexTree::getTestbitValue(const uint64_t key, const int width, const int curTestNo, int* bittestVal) { int shiftPos, maskPos = 0; bool bSuccess = true; if (!m_useMultiCol) { *bittestVal = 0; shiftPos = width - (curTestNo + 1) * 5; if (shiftPos > 0) *bittestVal = getBitValue(key, shiftPos, BIT_MASK_ARRAY[5]); else { if (shiftPos >= -4) { maskPos = width - curTestNo * 5; *bittestVal = key & BIT_MASK_ARRAY[maskPos]; } else bSuccess = false; } } else *bittestVal = m_multiColKey.testbitArray[curTestNo]; return bSuccess; } /*********************************************************** * DESCRIPTION: * Get the matching entry within the current tree node * PARAMETERS: * block - block data * fbo, sbid, entry - pointer address * width - key width * allocCount - the total number of allocated entries * entryMap - the entry availablibility map * checkEntry - bittest value * RETURN: * True if found, False otherwise * checkEntry - if found the ptr got reset ***********************************************************/ const bool IndexTree::getTreeMatchEntry(DataBlock* block, const uint64_t sbid, const uint64_t entry, const int width, const int allocCount, const bool* entryMap, int* matchEntry, IdxBitTestEntry* checkEntry) { IdxBitTestEntry curEntry; bool bFoundFlag = false; for (int i = 0; i < allocCount; i++) { getSubBlockEntry(block->data, sbid, entry + i, MAX_COLUMN_BOUNDARY, &curEntry); if (entryMap[i] && (curEntry.type == checkEntry->type && curEntry.bitTest == checkEntry->bitTest)) { *checkEntry = curEntry; *matchEntry = i; bFoundFlag = true; break; } } return bFoundFlag; } /*********************************************************** * DESCRIPTION: * Get tree node summary information * PARAMETERS: * block - block data * fbo, sbid, entry - pointer address * width - key width * group - entry group * testbitVal - current bit test value * RETURN: * NO_ERROR if success * error no if fail * allocCount - the total number of allocated entries * realCount - the total number of real entries * entryMap - the entry availablibility map ***********************************************************/ const int IndexTree::getTreeNodeInfo(DataBlock* block, const uint64_t sbid, const uint64_t entry, const int width, const IdxTreeGroupType group, int* allocCount, int* realCount, bool* entryMap) { IdxBitTestEntry curEntry; int rc = NO_ERROR; memset(entryMap, false, ENTRY_PER_SUBBLOCK); *realCount = 0; *allocCount = 0x1 << group; for (int i = 0; i < *allocCount; i++) { getSubBlockEntry(block->data, sbid, entry + i, MAX_COLUMN_BOUNDARY, &curEntry); if (curEntry.type == BIT_TEST || curEntry.type == LEAF_LIST) // every guy here must have the same type { entryMap[i] = true; *realCount = *realCount + 1; } else if (curEntry.type == EMPTY_ENTRY) entryMap[i] = false; else rc = ERR_IDX_TREE_INVALID_TYPE; if (rc != NO_ERROR) break; } return rc; } /*********************************************************** * DESCRIPTION: * Check index entry address pointer is empty or not * PARAMETERS: * pStruct - index entry pointer * entryType - the entry type * RETURN: * True if empty, False otherwise ***********************************************************/ const bool IndexTree::isAddrPtrEmpty(void* pStruct, const IdxTreeEntryType entryType) const { bool bStatus; IdxBitmapPointerEntry* pBitmap; IdxBitTestEntry* pBittest; IdxEmptyListEntry* pEmptyList; switch (entryType) { case BITMAP_PTR /*EMPTY_PTR*/: // this is the case for bitmap pointer address pBitmap = (IdxBitmapPointerEntry*)pStruct; bStatus = /*!pBitmap->oid &&*/ !pBitmap->fbo && !pBitmap->sbid && !pBitmap->entry; break; case BIT_TEST: // this is the case for bittest pointer address pBittest = (IdxBitTestEntry*)pStruct; bStatus = /*!pBittest->oid &&*/ !pBittest->fbo && !pBittest->sbid && !pBittest->entry; break; case EMPTY_LIST: // this is the case for bittest pointer address pEmptyList = (IdxEmptyListEntry*)pStruct; bStatus = !pEmptyList->fbo && !pEmptyList->sbid && !pEmptyList->entry; break; default: bStatus = true; } return bStatus; } const int IndexTree::initIndex(const FID treeFid, const FID listFid) { int rc = NO_ERROR; long numOfBlock; unsigned char writeBuf[BYTE_PER_BLOCK]; bool oldUseVb = BRMWrapper::getUseVb(); BRMWrapper::setUseVb(false); clear(); RETURN_ON_ERROR(openIndex(treeFid, listFid)); memset(writeBuf, 0, BYTE_PER_BLOCK); numOfBlock = getFileSize(m_cbTree.file.pFile) / BYTE_PER_BLOCK; for (int i = 0; i < numOfBlock; i++) fwrite(writeBuf, sizeof(writeBuf), 1, m_cbTree.file.pFile); numOfBlock = getFileSize(m_cbList.file.pFile) / BYTE_PER_BLOCK; for (int i = 0; i < numOfBlock; i++) fwrite(writeBuf, sizeof(writeBuf), 1, m_cbList.file.pFile); // very weird, have to close before we can call free mgr init closeIndex(); RETURN_ON_ERROR(openIndex(treeFid, listFid)); rc = m_freeMgr.init(m_cbTree, TREE); if (rc == NO_ERROR) rc = m_freeMgr.init(m_cbList, LIST); closeIndex(); BRMWrapper::setUseVb(oldUseVb); if (isDebug(DEBUG_1)) printf("\nEnd of the init for oid %d\n", m_cbTree.file.oid); return rc; } /*********************************************************** * DESCRIPTION: * Move tree entries * PARAMETERS: * fbo, sbid, entry - pointer address * width - key width * allocCount - the total number of allocated entries * entryMap - the entry availablibility map * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::moveEntry(const uint64_t oldFbo, const uint64_t oldSbid, const uint64_t oldEntry, const int width, const uint64_t newFbo, const uint64_t newSbid, const uint64_t newEntry, const int newGroup, const int allocCount, bool* entryMap, int* moveCount, const int newAllocCount) { int rc, i; DataBlock oldBlock, newBlock; IdxBitTestEntry curEntry, blankEntry; rc = readSubBlockEntry(m_cbTree, &oldBlock, oldFbo, oldSbid, oldEntry, /*width*/ MAX_COLUMN_BOUNDARY, &curEntry); if (rc != NO_ERROR) return rc; rc = readSubBlockEntry(m_cbTree, &newBlock, newFbo, newSbid, newEntry, /*width*/ MAX_COLUMN_BOUNDARY, &curEntry); if (rc != NO_ERROR) return rc; setBlankEntry(&blankEntry); for (i = 0; i < newAllocCount; i++) setSubBlockEntry(newBlock.data, newSbid, newEntry + i, MAX_COLUMN_BOUNDARY, &blankEntry); *moveCount = 0; for (i = 0; i < allocCount; i++) { getSubBlockEntry(oldBlock.data, oldSbid, oldEntry + i, MAX_COLUMN_BOUNDARY, &curEntry); if (entryMap[i]) { curEntry.group = newGroup; setSubBlockEntry(newBlock.data, newSbid, newEntry + *moveCount, MAX_COLUMN_BOUNDARY, &curEntry); *moveCount = *moveCount + 1; } setBlankEntry(&curEntry); if (newFbo != oldFbo) setSubBlockEntry(oldBlock.data, oldSbid, oldEntry + i, MAX_COLUMN_BOUNDARY, &curEntry); else setSubBlockEntry(newBlock.data, oldSbid, oldEntry + i, MAX_COLUMN_BOUNDARY, &curEntry); } rc = writeDBFile(m_cbTree, &newBlock, newFbo); if (rc != NO_ERROR) return rc; if (newFbo != oldFbo) rc = writeDBFile(m_cbTree, &oldBlock, oldFbo); // reset map memset(entryMap, false, ENTRY_PER_SUBBLOCK); for (i = 0; i < *moveCount; i++) entryMap[i] = true; return rc; } /*********************************************************** * DESCRIPTION: * Open index related files * PARAMETERS: * treeFid - the index tree file id * listFid - the index list file id * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::openIndex(const FID treeFid, const FID listFid) { int rc = NO_ERROR; clear(); // load index files #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID m_cbTree.file.pFile = m_fileopTree.openFile(treeFid); #endif if (m_cbTree.file.pFile == NULL) return ERR_FILE_OPEN; #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID m_cbList.file.pFile = m_fileopList.openFile(listFid); #endif if (m_cbList.file.pFile == NULL) { m_fileopTree.closeFile(m_cbTree.file.pFile); // close the one just open return ERR_FILE_OPEN; } m_cbTree.file.oid = treeFid; m_cbList.file.oid = listFid; uint64_t lbid0 = 0; #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID rc = BRMWrapper::getInstance()->getBrmInfo(m_cbTree.file.oid, 0, lbid0); #endif if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("\nFor oid : %d block zero is %ld", m_cbTree.file.oid, (long)lbid0); printf("\nIn open index, have problem in get brmInfo, rc=%d", rc); } return rc; } rc = readDBFile(m_cbTree, m_rootBlock.data, lbid0); return rc; } /*********************************************************** * DESCRIPTION: * Process index, including search or delete * PARAMETERS: * key - key value * width - key width * rid - row id * listHdrAddr - list header address * bDelete - delete operation flag * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::processIndex(const uint64_t key, const int width, const RID rid, IdxEmptyListEntry& listHdrAddr, const bool bDelete) { int loopCount, testbitVal, i, allocCount, realCount, matchPos, curFbo, curSbid, curEntry; bool bSuccess; IdxEmptyListEntry listEntry; IdxBitTestEntry bittestEntry, matchBitTestEntry; DataBlock curBlock; bool bFound, entryMap[ENTRY_PER_SUBBLOCK]; int rootTestbitVal, rc = NO_ERROR; IdxBitmapPointerEntry bitmapEntry; getTestbitValue(key, width, 0, &rootTestbitVal); getSubBlockEntry(m_rootBlock.data, 1, rootTestbitVal, MAX_COLUMN_BOUNDARY, &bitmapEntry); if (isAddrPtrEmpty(&bitmapEntry, /*EMPTY_PTR*/ BITMAP_PTR)) return bDelete ? ERR_IDX_LIST_INVALID_DELETE : NOT_FOUND; // get the rootbittestEntry level bitmapPointerMap curFbo = bitmapEntry.fbo; curSbid = bitmapEntry.sbid; curEntry = bitmapEntry.entry; loopCount = width / 5 + 1; for (i = 1; i < loopCount; i++) { // load the block rc = readSubBlockEntry(m_cbTree, &curBlock, curFbo, curSbid, curEntry, /*width*/ MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; if (isDebug(DEBUG_3)) { printf("\nIn processIndex, level %d", i); printSubBlock(curFbo, curSbid); } rc = getTreeNodeInfo(&curBlock, curSbid, curEntry, width, (IdxTreeGroupType)bittestEntry.group, &allocCount, &realCount, entryMap); if (rc != NO_ERROR) return rc; matchBitTestEntry = bittestEntry; // assign to the value of the first entry bSuccess = getTestbitValue(key, width, i, &testbitVal); matchBitTestEntry.bitTest = testbitVal; bFound = getTreeMatchEntry(&curBlock, curSbid, curEntry, width, allocCount, entryMap, &matchPos, &matchBitTestEntry); if (bFound) // this test bit exists at the current level { if (i == loopCount - 1) { // if( loopCount == 2 ) // because of bitmap, it requires a special treatment curEntry += matchPos; break; } } else return NOT_FOUND; getSubBlockEntry(&curBlock, curSbid, curEntry + matchPos, MAX_COLUMN_BOUNDARY, &matchBitTestEntry); curFbo = matchBitTestEntry.fbo; curSbid = matchBitTestEntry.sbid; curEntry = matchBitTestEntry.entry; } // end of for( i = 0; // here's the last level // load the last piece rc = readSubBlockEntry(m_cbTree, &curBlock, curFbo, curSbid, curEntry /*+ matchPos*/, MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; listEntry.fbo = bittestEntry.fbo; listEntry.sbid = bittestEntry.sbid; listEntry.entry = bittestEntry.entry; if (bDelete) { rc = m_listMgr.deleteIndexList(m_cbList, rid, key, &listEntry); if (rc != NO_ERROR) return rc; bSuccess = getTestbitValue(key, width, loopCount - 1, &testbitVal); setBittestEntry(&bittestEntry, testbitVal, bittestEntry.group, listEntry.fbo, listEntry.sbid, listEntry.entry, LEAF_LIST); setSubBlockEntry(&curBlock, curSbid, curEntry, MAX_COLUMN_BOUNDARY, &bittestEntry); rc = writeDBFile(m_cbTree, &curBlock, curFbo); } else listHdrAddr = listEntry; return rc; } /*********************************************************** * DESCRIPTION: * Print a sub block * PARAMETERS: * fbo - file block offset * sbid - sub block id * RETURN: * none ***********************************************************/ void IndexTree::printSubBlock(const int fbo, const int sbid, const bool bNoZero) { DataBlock curBlock; readDBFile(m_cbTree, &curBlock, fbo); printf("\n lbid=%2d sbid=%2d", fbo, sbid); printMemSubBlock(&curBlock, sbid); } void IndexTree::printMemSubBlock(DataBlock* curBlock, const int sbid, const bool bNoZero) { int off; unsigned char* curPos; IdxBitTestEntry curEntry, testZero; setBlankEntry(&testZero); curPos = curBlock->data + BYTE_PER_SUBBLOCK * sbid; printf("\n========================"); for (int i = 0; i < ENTRY_PER_SUBBLOCK; i++) { memcpy(&curEntry, curPos, MAX_COLUMN_BOUNDARY); off = memcmp(&testZero, &curEntry, MAX_COLUMN_BOUNDARY); printf("\n Entry %2d : ", i); for (int j = 0; j < MAX_COLUMN_BOUNDARY; j++) printf(" %2X", *(curPos + j)); printf(" fbo=%2d sbid=%2d entry=%2d group=%d bit=%2d type=%2d", (int)curEntry.fbo, (int)curEntry.sbid, (int)curEntry.entry, (int)curEntry.group, (int)curEntry.bitTest, (int)curEntry.type); curPos += MAX_COLUMN_BOUNDARY; } printf("\n"); } /*********************************************************** * DESCRIPTION: * Assign segment from free manager * PARAMETERS: * segmentType - group type * assignPtr - the assigned ptr * no - internal debug use flag * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::releaseSegment(int segmentType, IdxEmptyListEntry* myPtr) { int rc = NO_ERROR; m_rootBlock.dirty = true; if (m_useFreeMgr) { if (isDebug(DEBUG_3)) printf("\n----------Release the pointer, entry segment=%d fbo=%d sbid=%d entry=%d", segmentType, (int)myPtr->fbo, (int)myPtr->sbid, (int)myPtr->entry); rc = m_freeMgr.releaseSegment(m_cbTree, &m_rootBlock, TREE, (IdxTreeGroupType)segmentType, myPtr /*, TREE */); if (isDebug(DEBUG_3)) printf("\nReleased"); } return rc; } const int IndexTree::resetIndexFile(const FID treeFid, const FID listFid) { /* long numOfBlock; unsigned char writeBuf[BYTE_PER_BLOCK]; memset( writeBuf, 0, BYTE_PER_BLOCK ); numOfBlock = getFileSize( m_cbTree.file.pFile )/BYTE_PER_BLOCK; for( int i = 0; i < numOfBlock; i++ ) fwrite( writeBuf, sizeof( writeBuf ), 1, m_cbTree.file.pFile ); numOfBlock = getFileSize( m_cbList.file.pFile )/BYTE_PER_BLOCK; for( int i = 0; i < numOfBlock; i++ ) fwrite( writeBuf, sizeof( writeBuf ), 1, m_cbList.file.pFile ); */ return initIndex(treeFid, listFid); } /*********************************************************** * DESCRIPTION: * Calculate bit test array for multi column index * PARAMETERS: * none * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::calculateBittestArray() { if (m_multiColKey.totalBit <= 0) return ERR_INVALID_PARAM; m_multiColKey.maxLevel = m_multiColKey.totalBit / 5 + 1; if (m_multiColKey.maxLevel > IDX_MAX_MULTI_COL_IDX_LEVEL) return ERR_VALUE_OUTOFRANGE; for (int i = 0; i < m_multiColKey.maxLevel - 1; i++) { m_multiColKey.curBitset = (m_multiColKey.bitSet & m_multiColKey.curMask) >> (IDX_MAX_MULTI_COL_BIT - (i + 1) * 5); m_multiColKey.testbitArray[i] = m_multiColKey.curBitset.to_ulong(); m_multiColKey.curMask = m_multiColKey.curMask >> 5; } m_multiColKey.curBitset = (m_multiColKey.bitSet & m_multiColKey.curMask) >> (IDX_MAX_MULTI_COL_BIT - m_multiColKey.totalBit); m_multiColKey.testbitArray[m_multiColKey.maxLevel - 1] = m_multiColKey.curBitset.to_ulong(); return NO_ERROR; } /*********************************************************** * DESCRIPTION: * Setup bit test array for multi column index * PARAMETERS: * keyArray - index key array * totalByte - total byte in the key * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::setBitsetColumn(void* val, const int pos, const int width, const ColType colType) { int copyLen = width / 8; switch (colType) { // No body is using float or double for indexing /* case WriteEngine::WR_FLOAT : m_multiColKey.curBitset = *((float*) val); break; case WriteEngine::WR_DOUBLE : m_multiColKey.curBitset = *((double*) val); break; */ case WriteEngine::WR_CHAR: m_multiColKey.curBitset.reset(); for (int i = 0; i < width / 8; i++) { uint64_t curChar = ((char*)val)[i]; m_multiColKey.curBitset = m_multiColKey.curBitset << 8; m_multiColKey.curBitset |= curChar; } memcpy(m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (char*)val, copyLen); break; case WriteEngine::WR_SHORT: m_multiColKey.curBitset = *((short*)val); memcpy(m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (short*)val, copyLen); break; case WriteEngine::WR_BYTE: m_multiColKey.curBitset = *((char*)val); memcpy(m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (char*)val, copyLen); break; case WriteEngine::WR_LONGLONG: m_multiColKey.curBitset = *((long long*)val); memcpy(m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (long long*)val, copyLen); break; case WriteEngine::WR_INT: case WriteEngine::WR_MEDINT: default: memcpy(m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (int*)val, copyLen); m_multiColKey.curBitset = *((int*)val); break; } m_multiColKey.totalBit += width; m_multiColKey.curBitset = m_multiColKey.curBitset << (IDX_MAX_MULTI_COL_BIT - m_multiColKey.totalBit); m_multiColKey.bitSet |= m_multiColKey.curBitset; return NO_ERROR; } /*********************************************************** * DESCRIPTION: * Set bit test entry * PARAMETERS: * bittestEntry - the bit test entry * testbitVal - current bit test value * group - entry group * fbo, sbid, entry - pointer address * RETURN: * none ***********************************************************/ void IndexTree::setBittestEntry(IdxBitTestEntry* bittestEntry, const uint64_t testbitVal, const uint64_t group, const uint64_t fbo, const uint64_t sbid, const uint64_t entry, const uint64_t entryType) const { bittestEntry->type = entryType; bittestEntry->bitTest = testbitVal; bittestEntry->group = group; bittestEntry->bitCompare = BIT_5; bittestEntry->spare = 0; bittestEntry->fbo = fbo; bittestEntry->sbid = sbid; bittestEntry->entry = entry; } /*********************************************************** * DESCRIPTION: * Set empty list entry for free manager * PARAMETERS: * bittestEntry - the bit test entry * testbitVal - current bit test value * group - entry group * fbo, sbid, entry - pointer address * RETURN: * none ***********************************************************/ // todo: need test case void IndexTree::setEmptyListEntry(IdxEmptyListEntry* myEntry, const uint64_t group, const uint64_t fbo, const uint64_t sbid, const uint64_t entry) const { myEntry->type = EMPTY_PTR; myEntry->group = group; myEntry->spare = 0; myEntry->spare2 = 0; myEntry->fbo = fbo; myEntry->sbid = sbid; myEntry->entry = entry; } /*********************************************************** * DESCRIPTION: * Set the tree header node * PARAMETERS: * myTree - tree pointer * key - index key * width - key width * rid - ROW ID * testbitVal - bit test value at root level * bitmapEntry - bitmap entry * RETURN: * none ***********************************************************/ void IndexTree::setTreeHeader(IdxTree* myTree, const uint64_t key, const RID rid, const int width, const int testbitVal, const IdxBitmapPointerEntry bitmapEntry) { IdxBitTestEntry nextEntry, curEntry; myTree->width = width; myTree->key = key; myTree->rid = rid; myTree->maxLevel = (width / 5) + 1; setBlankEntry(&nextEntry); setBlankEntry(&curEntry); // myEntry.bitTest = testbitVal; nextEntry.fbo = bitmapEntry.fbo; nextEntry.sbid = bitmapEntry.sbid; nextEntry.entry = bitmapEntry.entry; nextEntry.type = BIT_TEST; // nextEntry.group = ENTRY_32; curEntry.fbo = 0; curEntry.sbid = IDX_BITMAP_SUBBLOCK_NO; curEntry.entry = testbitVal; curEntry.type = BITMAP_PTR; curEntry.group = ENTRY_32; curEntry.bitTest = testbitVal; // at this point useCount not known setTreeNode(&myTree->node[0], 0, ENTRY_PER_SUBBLOCK, 0, testbitVal, nextEntry, curEntry); } /*********************************************************** * DESCRIPTION: * Set the tree node * PARAMETERS: * myNode - node pointer * level - current tree level * allocCount - allocated count * useCount - used count * offset - entry offset * nextEntry - next entry * curEntry - current entry * RETURN: * none ***********************************************************/ void IndexTree::setTreeNode(IdxTreeNode* myNode, const int level, const int allocCount, const int useCount, const int offset, const IdxBitTestEntry nextEntry, const IdxBitTestEntry curEntry) { myNode->level = level; myNode->allocCount = allocCount; myNode->useCount = useCount; myNode->offset = offset; myNode->next = nextEntry; myNode->current = curEntry; myNode->used = true; } const bool IndexTree::isTreeEmpty() { bool bEmpty = true; IdxBitmapPointerEntry curBitmapPointer, emptyPointer; memset(&emptyPointer, 0, MAX_COLUMN_BOUNDARY); for (int i = 0; i < 32; i++) { getSubBlockEntry(m_rootBlock.data, IDX_BITMAP_SUBBLOCK_NO, i, MAX_COLUMN_BOUNDARY, &curBitmapPointer); if (memcmp(&curBitmapPointer, &emptyPointer, MAX_COLUMN_BOUNDARY)) { bEmpty = false; break; } } // printMemSubBlock( &m_rootBlock, IDX_BITMAP_SUBBLOCK_NO ); return bEmpty; } /*********************************************************** * DESCRIPTION: * Allocate Row ID * PARAMETERS: * tableFid - the file id for table bitmap file * totalRow - the total number of rows need to be allocated * RETURN: * NO_ERROR if success * rowIdArray - allocation of the row id left here ***********************************************************/ const int IndexTree::updateIndex(const uint64_t key, const int width, const RID rid) { int rootTestbitVal, rc = NO_ERROR; IdxBitmapPointerEntry curBitmapPointer; clearTree(&m_tree); getTestbitValue(key, width, 0, &rootTestbitVal); getSubBlockEntry(m_rootBlock.data, IDX_BITMAP_SUBBLOCK_NO, rootTestbitVal, MAX_COLUMN_BOUNDARY, &curBitmapPointer); setTreeHeader(&m_tree, key, rid, width, rootTestbitVal, curBitmapPointer); if (m_tree.maxLevel > IDX_MAX_TREE_LEVEL) return ERR_VALUE_OUTOFRANGE; if (isAddrPtrEmpty(&curBitmapPointer, (IdxTreeEntryType)curBitmapPointer.type)) rc = buildEmptyIndexTreeBranch(key, width, rid, rootTestbitVal); else // at least we have the root level pointer rc = buildExistIndexTreeBranch(key, width, rid, rootTestbitVal, curBitmapPointer); return rc; } /*********************************************************** * DESCRIPTION: * Dummy list addr generator * PARAMETERS: * key - index key * width - key width * rid - ROW ID * myEntry - return entry with addr * no - sequence no * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::updateIndexList(const uint64_t key, const int width, const RID rid, IdxEmptyListEntry* myEntry, const int no, const bool addFlag) { int rc = NO_ERROR; if (m_useListMgr) { // doing something here if (!addFlag) // update list { if (m_useMultiRid) rc = m_listMgr.updateIndexList(m_cbList, m_multiRid, key, myEntry); else rc = m_listMgr.updateIndexList(m_cbList, rid, key, myEntry); } else // add list { if (m_useMultiRid) rc = m_listMgr.addIndexListHdr(m_cbList, m_multiRid, key, myEntry); else rc = m_listMgr.addIndexListHdr(m_cbList, rid, key, myEntry); } } else { myEntry->fbo = 3; myEntry->sbid = 3; myEntry->entry = 3 + no; } return rc; } /*********************************************************** * DESCRIPTION: * Update the entry in index list file * PARAMETERS: * key - index key * width - key width * rid - ROW ID * curLevel - current tree level * group - current group * allocCount - allocated count * useCount - used count * offset - entry offset * RETURN: * NO_ERROR if success * error no if fail ***********************************************************/ const int IndexTree::updateListFile(const uint64_t key, const int width, const RID rid, const int curLevel, const uint64_t group, const int allocCount, const int useCount, const int offset, const bool addFlag) { int rc, testbitVal, parentLevel = curLevel - 1; IdxEmptyListEntry listEntry; IdxBitTestEntry bittestEntry, curEntry; bool bSuccess; DataBlock curBlock; rc = readSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry + offset, MAX_COLUMN_BOUNDARY, &bittestEntry); if (rc != NO_ERROR) return rc; if (!addFlag) { listEntry.fbo = bittestEntry.fbo; listEntry.sbid = bittestEntry.sbid; listEntry.entry = bittestEntry.entry; } rc = updateIndexList(key, width, rid, &listEntry, curLevel, addFlag); if (rc != NO_ERROR) return rc; bSuccess = getTestbitValue(key, width, curLevel, &testbitVal); setBittestEntry(&bittestEntry, testbitVal, group, listEntry.fbo, listEntry.sbid, listEntry.entry, LEAF_LIST); rc = writeSubBlockEntry(m_cbTree, &curBlock, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry + offset, MAX_COLUMN_BOUNDARY, &bittestEntry); setBittestEntry(&curEntry, testbitVal, group, m_tree.node[parentLevel].next.fbo, m_tree.node[parentLevel].next.sbid, m_tree.node[parentLevel].next.entry, LEAF_LIST); setTreeNode(&m_tree.node[curLevel], curLevel, allocCount, useCount, offset, bittestEntry, curEntry); return rc; } } // namespace WriteEngine