// Get the latest data page within which we store data node
boolean pageFixed = false;
//add by stanley
boolean hasPageForOverflow = false;
PageNumber PgnForOverflow = null;
DataPage DataPageForOverflow = null;
Lock lockforOverflow = null;
PageNumber latestDataPageNumber = buffer.getLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
Lock lock = null;
boolean isValidPage;
while (latestDataPageNumber != null && latestDataPageNumber.getPageNumber() > 0) {
lock = BTreeLock.acquire(kContext, LockManager.LOCK_WAITING, latestDataPageNumber, LockManager.LOCK_WRITE);
//Usually the page we get should not stay at freelist. But there is one exception:
//With txn, each txn get LDP from GLDP list. Thus assuming
//(1) txn1 get a LDP (pageNumber) from GLDP list and remove it from the list and
//paused due to OS scheduler
//(2) At this moment, txn2 deletes all nodes in this page
//, finds that it is not a LDP by isLatestDataPage() (checking a page in its own LDP
//list and GLDP list), then frees this page.
//(3) Later txn1 resumes getting the LDP page with the pagenumber it got at (1), then
// finds it has been freed. The following exception occurs!!!!!!!!
//Hereby, when we get a LDP, we must catch this exception and get a new LDP. See codes
try {
isValidPage = true;
dataPage = new DataPage(btreeSpec.btree.getBtreeId(), latestDataPageNumber, btreeSpec, buffer);
} catch (ChaiDBException e) {
if (e.getErrorCode() != ErrorCode.BTREE_USE_FREEPAGE) {
logger.error(e);
throw e;
}
isValidPage = false;
}
/* we abide by the following algorithm to get a data page
* 1.if the latest data page is big enough to hold all the new datanode then use it. else go to 2
* 2.if a new data page is big enough to hold all the new datanode then use it. else go to 3
* 3.if the latest data page is big enough to hold BTreeSpec.DATA_NODE_HEADER_SIZE + 4 then use it. else go to 2
* 4.we new a data page and use it.
*/
if (isValidPage && !dataPage.isOverflow()) {
if (dataPage.getFreeSpace() >= newNodeSize) {
if (!(btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE)) {
pageFixed = true;
break;
} else { // if this tree is special, this data page must
// belong to this doc if there are still data in
// this datapage
int docID = ((NodeId) key).getDocId();
if (dataPage.getCurrNodeNumbers() == 0) {
dataPage.setDocID(docID);
pageFixed = true;
break;
} else {
if (dataPage.isOfDoc(docID)) {
pageFixed = true;
break;
} else {
// release the data page
buffer.releasePage(latestDataPageNumber
.getTreeId(), latestDataPageNumber, false);
// because we did NOT update this page, so we
// release it at once
BTreeLock.release(kContext, pageNumber
.getTreeId(), lock);
latestDataPageNumber = buffer
.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
}
}
}
} else if (!hasPageForOverflow && dataPage.getFreeSpace() >= BTreeSpec.DATA_NODE_HEADER_SIZE + 4) {
if (!(btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE)) {
hasPageForOverflow = true;
PgnForOverflow = latestDataPageNumber;
DataPageForOverflow = dataPage;
lockforOverflow = lock;
} else { // if this tree is special, this data page must
// belong to this doc if there are still data in
// this datapage
int docID = ((NodeId) key).getDocId();
if (dataPage.getCurrNodeNumbers() == 0) {
dataPage.setDocID(docID);
hasPageForOverflow = true;
PgnForOverflow = latestDataPageNumber;
DataPageForOverflow = dataPage;
lockforOverflow = lock;
} else {
if (dataPage.isOfDoc(docID)) {
hasPageForOverflow = true;
PgnForOverflow = latestDataPageNumber;
DataPageForOverflow = dataPage;
lockforOverflow = lock;
} else {
// release the data page
buffer.releasePage(latestDataPageNumber
.getTreeId(), latestDataPageNumber, false);
// because we did NOT update this page, so we
// release it at once
BTreeLock.release(kContext, pageNumber
.getTreeId(), lock);
}
}
}
latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
} else {
//The datapage is not suitable then release it
buffer.releasePage(latestDataPageNumber.getTreeId(), latestDataPageNumber, false);
//because we did NOT update this page, so we release it at once
BTreeLock.release(kContext, pageNumber.getTreeId(), lock);
latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
}
} else {
//The datapage is not suitable then release it
buffer.releasePage(latestDataPageNumber.getTreeId(), latestDataPageNumber, false);
//because we did NOT update this page, so we release it at once
BTreeLock.release(kContext, pageNumber.getTreeId(), lock);
latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
}
}
// size in byte of the node needed
if (!pageFixed) {
if (!hasPageForOverflow) {
dataPage = DataPage.newPage(btreeSpec, buffer, false, kContext, docid);
if (btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE) dataPage.setDocID(docid);
} else {
if (newNodeSize <= BTreeSpec.PAGE_SIZE - BTreeSpec.PAGE_HEADER_SIZE) {
dataPage = DataPage.newPage(btreeSpec, buffer, false, kContext, docid);
if (btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE) dataPage.setDocID(docid);
buffer.releasePage(PgnForOverflow.getTreeId(), PgnForOverflow, false);
// because we did NOT update this page, so we release it
// at once
BTreeLock.release(kContext, pageNumber.getTreeId(), lockforOverflow);
} else {
dataPage = DataPageForOverflow;
latestDataPageNumber = PgnForOverflow;
lock = lockforOverflow;
}
}
// lock=returnLock[0];
} else {
if (hasPageForOverflow) {
buffer.releasePage(PgnForOverflow.getTreeId(), PgnForOverflow, false);
//because we did NOT update this page, so we release it at once
BTreeLock.release(kContext, pageNumber.getTreeId(), lockforOverflow);
}
}
/*Modified by ben zhang at Aug, 12, 2002 */
int nodeSize = BTreeSpec.NODE_HEADER_SIZE + key.size();
/* begin : added by marriane 2001-12-28 for bind all insert
* node log records
*/
if (needLog) {
bindAllInsertNodeLogRecords(dataPage, data, nodeSize, keyExist, key, txnId, nextIndex, currNode);
}
/* end : added by marriane 2001-12-28 for bind all insert
* node log records
*/
setUpperBound((short) (upperBound - btreeSpec.getLeafNodeSize()));
setLowerBound((short) (lowerBound + 2));
if (nextIndex < currNode) {
System.arraycopy(page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), page, (BTreeSpec.PAGE_HEADER_SIZE + (nextIndex + 1) * 2), (currNode - nextIndex) * 2);
}
System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), 2);
// insert data node in data page
/* Modified by ben zhang at Aug, 12, 2002 Pending issue
* whether key is necessary
*/
// @@@ Modified by Kurt
byte[] newPageOff = null;
short newOffset = 0;
if (btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
newPageOff = insertDupNode(dataPage, key, data, mode, kContext, -1);
} else {
newOffset = dataPage.insertNode(key.toBytes(), data, mode, kContext, -1, (byte) 0);
}
//
// short dataNodeOff=dataPage.insertNode(key,data,mode,kContext,-1);
// @@@ Modified by Kurt
// unfix the page
buffer.releasePage(dataPage.pageNumber.getTreeId(), dataPage.pageNumber, true);
// create a new BTree leaf node
/*Modified by ben zhang at aug, 12, 2002 */
leafNode = BTreeNode.createNewBTreeNode(this, upperBound, key.toBytes(), dataPage.getPageNumber());
if (nodeSize <= btreeSpec.getLeafNodeSize()) {
// if the page has enough space to hold the new node
leafNode.setFlags((byte) 0);
} else {
// The leaf page doesn't have enough space to hold data
leafNode.setFlags((byte) 3);
}
short dataNodeOff = 0;
// @@@ Modified by Kurt
if (btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
if (newPageOff.length >= 4) {
dataNodeOff = (short) ByteTool.bytesToInt(newPageOff, 0, btreeSpec.isMsbFirst());
leafNode.setDataNodeOffset(dataNodeOff);
}
if (newPageOff.length == 8) {
int newPageNum = ByteTool.bytesToInt(newPageOff, 4, btreeSpec.isMsbFirst());
leafNode.setPageNumber(newPageNum);
}
} else {
leafNode.setDataNodeOffset(newOffset);
}
// @@@ Modified by Kurt
// We only need to store key in the leaf page now
/*Modified by ben zhang at aug, 12, 2002 */
leafNode.setInternalNode(key.toBytes(), kContext);
//reput it to LDPL if this datapage is allocated this time
if (!pageFixed) {
if (dataPage.isOverflow()) {
String details = Debug.getDebugInfo() + " want to put overflowpage[" + dataPage.pageNumber + "] to LDPL.";
throw new ChaiDBException(ErrorCode.BTREE_DEBUG, details);
}
buffer.putLatestDataPage(docid, dataPage.pageNumber, needLog ? new Integer(txnId) : null);
} else {
/* if the following statement is not within "else",
* the below line must be uncommented
*/
if (needLog) BTreeLock.change(kContext, lock, latestDataPageNumber.getTreeId(), LockManager.LOCK_READ);
else BTreeLock.release(kContext, pageNumber.getTreeId(), lock);
}
}