From 6459edd39edfc1679babdac2c6fa626668a492e9 Mon Sep 17 00:00:00 2001 From: unknown <707316569@qq.com> Date: Sat, 12 Aug 2023 20:54:14 +0800 Subject: [PATCH 1/4] add code comments for UBtree Recycle Queue --- .../storage/access/ubtree/ubtrecycle.cpp | 164 +++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-) diff --git a/src/gausskernel/storage/access/ubtree/ubtrecycle.cpp b/src/gausskernel/storage/access/ubtree/ubtrecycle.cpp index 36a7dc558..3b7d3f2e3 100644 --- a/src/gausskernel/storage/access/ubtree/ubtrecycle.cpp +++ b/src/gausskernel/storage/access/ubtree/ubtrecycle.cpp @@ -79,6 +79,18 @@ static UBTRecycleQueueItem HeaderGetItem(UBTRecycleQueueHeader header, uint16 of pg_unreachable(); /* won't reach here */ } +/* + * UBTreeInitRecycleQueuePage + * + * Initialize a page of the recycle queue + * + * This function will be called when the system is initializing the recycle queue, + * or extend one page of the recycle queue. + * The function will initialize the page usiong the starting value, if it is a meta + * page, the functionm will initialize the meta data. + * + * NOTE: the pages in URQ doesn't have special space. + */ static void UBTreeInitRecycleQueuePage(Relation rel, Page page, Size size, BlockNumber blkno) { PageInit(page, size, 0); @@ -113,6 +125,7 @@ static void UBTreeInitRecycleQueuePage(Relation rel, Page page, Size size, Block } } +/* check whether the URQ has been initialized */ static bool RecycleQueueInitialized(Relation rel) { /* open smgr, might have to re-open if a cache flush happened */ @@ -219,6 +232,15 @@ static void InitRecycleQueueInitialPage(Relation rel, Buffer buf) END_CRIT_SECTION(); } +/* + * ReadRecycleQueueBuffer + * + * Read one page of the recycle queue and return it. + * + * We use forknum FSM_FORKNUM to acquire the page of the recycle queue. Note that + * the files of this forknumber do not actually store the structure of fsm, they + * store URQ instead. + */ Buffer ReadRecycleQueueBuffer(Relation rel, BlockNumber blkno) { Buffer buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_NORMAL, NULL); @@ -231,6 +253,19 @@ Buffer ReadRecycleQueueBuffer(Relation rel, BlockNumber blkno) return buf; } +/* + * UBTreeInitializeRecycleQueue + * + * Initialize a recycle queue for a certain UBtree relation + * + * This is the entrance function of the URQ, and will be called during UBTreeLoad, + * which is a subroutine of constructing a UBtree index. The function will also be + * called if we need to use URQ but it hasn't been initialized. + * This function will create two recycle queues and initialize them, one is Potential + * Empty Page Queue which has pages 0 2 4, and the other is Available Page Queue which + * has pages 1 3 5. First, check existing pages, and if the pages are not enough, then + * create necessary pages(URQ has at least 6 pages). + */ void UBTreeInitializeRecycleQueue(Relation rel) { LockRelationForExtension(rel, ExclusiveLock); @@ -306,6 +341,7 @@ static bool UBTreeTryRecycleEmptyPageInternal(Relation rel) return true; } +/* Try to move pages from Potential Empty Page Queue to Available Page Queue */ void UBTreeTryRecycleEmptyPage(Relation rel) { bool firstTrySucceed = UBTreeTryRecycleEmptyPageInternal(rel); @@ -315,16 +351,34 @@ void UBTreeTryRecycleEmptyPage(Relation rel) } } +/* + * UBTreeRecordFreePage + * + * Put a freed page into the Available Page Queue + * + * This function will be called if the page has been deleted and moved out of + * the UBtree, by the function UBTreeVacuumPage. We can safely put it in URQ + * for reuse. + */ void UBTreeRecordFreePage(Relation rel, BlockNumber blkno, TransactionId xid) { UBTreeRecycleQueueAddPage(rel, RECYCLE_FREED_FORK, blkno, xid); } +/* + * UBTreeRecordEmptyPage + * + * Put a (possibly) empty page into the Potential Empty Page Queue + * + * This function will be called if the page's opaque->activeTupleCount = 0, as + * a subroutine of UBTreeDoDelete. + */ void UBTreeRecordEmptyPage(Relation rel, BlockNumber blkno, TransactionId xid) { UBTreeRecycleQueueAddPage(rel, RECYCLE_EMPTY_FORK, blkno, xid); } +/* When we reuse a UBtree page from URQ, we remove the item which matches the page. */ void UBTreeRecordUsedPage(Relation rel, UBTRecycleQueueAddress addr) { if (addr.queueBuf != InvalidBuffer) { @@ -332,6 +386,7 @@ void UBTreeRecordUsedPage(Relation rel, UBTRecycleQueueAddress addr) } } +/* Step next page behind current page buf */ static Buffer StepNextPage(Relation rel, Buffer buf) { Page page = BufferGetPage(buf); @@ -346,6 +401,17 @@ static Buffer StepNextPage(Relation rel, Buffer buf) return nextBuf; } +/* + * GetAvailablePageOnPage + * + * Get an available UBTree page info from the URQ page. + * + * Here, the function name means: get an available "UBtree Page" information from + * a "URQ Page". + * We get the header of the page and check page info items. If we find a proper + * UBtree page, we collect its info in the parameter addr and return it. Else, we + * return InvalidBuffer. + */ static Buffer GetAvailablePageOnPage(Relation rel, UBTRecycleForkNumber forkNumber, Buffer buf, TransactionId WaterLevelXid, UBTRecycleQueueAddress *addr, bool *continueScan) { @@ -356,6 +422,12 @@ static Buffer GetAvailablePageOnPage(Relation rel, UBTRecycleForkNumber forkNumb while (IsNormalOffset(curOffset)) { UBTRecycleQueueItem item = HeaderGetItem(header, curOffset); if (TransactionIdFollowsOrEquals(item->xid, WaterLevelXid)) { + /* + * We should know that the item->xid in the URQ are increasing, so the xid + * of the latter items are greater than or equal to item->xid here, and + * also the WaterLevelXid. So we needn't to scan the latter items in the + * URQ, and just return InvalidBuffer. + */ *continueScan = false; return InvalidBuffer; } @@ -370,6 +442,7 @@ static Buffer GetAvailablePageOnPage(Relation rel, UBTRecycleForkNumber forkNumb if (forkNumber == RECYCLE_EMPTY_FORK || UBTreePageRecyclable(BufferGetPage(targetBuf))) { WHITEBOX_TEST_STUB("GetAvailablePageOnPage-got", WhiteboxDefaultErrorEmit); + // Here we get an available UBtree page info, so we store it and return *continueScan = false; addr->queueBuf = buf; addr->indexBlkno = item->blkno; @@ -392,6 +465,18 @@ static Buffer GetAvailablePageOnPage(Relation rel, UBTRecycleForkNumber forkNumb return InvalidBuffer; } +/* + * UBTreeGetAvailablePage + * + * Get an available page from the URQ(Always Available Page Queue) + * + * This is the function which we use to acquire used pages from URQ for recycle. + * 1.We first get the head page of the URQ, and then get an available page from it, + * if we couldn't get an available page, we step to next queue page. + * 2.If we get an available page, we can return the page. If we couldn't get one and + * the forknumber is EMPTY, we just return InvalidBuffer. Else, do step 3. + * 3.Here, forknumber is FREED, we check newly created pages in MAIN_FORKNUM. + */ Buffer UBTreeGetAvailablePage(Relation rel, UBTRecycleForkNumber forkNumber, UBTRecycleQueueAddress *addr) { TransactionId frozenXid = g_instance.undo_cxt.oldestFrozenXid; @@ -399,6 +484,7 @@ Buffer UBTreeGetAvailablePage(Relation rel, UBTRecycleForkNumber forkNumber, UBT TransactionId waterLevelXid = ((forkNumber == RECYCLE_EMPTY_FORK) ? recycleXid : frozenXid); + // Get the head page of the URQ. Buffer queueBuf = RecycleQueueGetEndpointPage(rel, forkNumber, true, BT_READ); Buffer indexBuf = InvalidBuffer; @@ -432,6 +518,7 @@ Buffer UBTreeGetAvailablePage(Relation rel, UBTRecycleForkNumber forkNumber, UBT Buffer metaBuf = ReadRecycleQueueBuffer(rel, metaBlockNumber); LockBuffer(metaBuf, BT_READ); UBTRecycleMeta metaData = (UBTRecycleMeta)PageGetContents(BufferGetPage(metaBuf)); + // NOTE: At the time we inited the URQ, we executed meta->nblocksUpper = smgrnblocks(rel->rd_smgr, MAIN_FORKNUM); for (BlockNumber curBlkno = metaData->nblocksUpper; curBlkno < nblocks; curBlkno++) { if (metaData->nblocksUpper > curBlkno) { continue; @@ -460,14 +547,25 @@ Buffer UBTreeGetAvailablePage(Relation rel, UBTRecycleForkNumber forkNumber, UBT return indexBuf; } +/* + * UBTreeRecycleQueuePageChangeEndpointLeftPage + * + * 1. isHead = true, it means that buf is the old head page, so remove + * its flag and set its head and tail + * 2. isHead = false, it means that buf is the old tail page, so remove + * its flag and set tailItem->next to indicate the next item is on + * the next new page. + */ void UBTreeRecycleQueuePageChangeEndpointLeftPage(Buffer buf, bool isHead) { uint32 endpointFlag = (isHead ? URQ_HEAD_PAGE : URQ_TAIL_PAGE); UBTRecycleQueueHeader header = GetRecycleQueueHeader(BufferGetPage(buf), BufferGetBlockNumber(buf)); if (isHead) { + /* buf is old head page */ header->head = InvalidOffset; header->tail = InvalidOffset; } else { + /* buf is old tail page */ Assert(IsNormalOffset(header->tail)); UBTRecycleQueueItem tailItem = HeaderGetItem(header, header->tail); tailItem->next = OtherBlockOffset; @@ -475,11 +573,20 @@ void UBTreeRecycleQueuePageChangeEndpointLeftPage(Buffer buf, bool isHead) header->flags &= ~endpointFlag; } +/* + * UBTreeRecycleQueuePageChangeEndpointRightPage + * + * 1. isHead = true, it means that buf is the new head page, so set its + * flag and set its headItem->prev or header->head + * 2. isHead = false, it means that buf is the new tail page, so set its + * flag and check whether it has no item. + */ void UBTreeRecycleQueuePageChangeEndpointRightPage(Buffer buf, bool isHead) { uint32 endpointFlag = (isHead ? URQ_HEAD_PAGE : URQ_TAIL_PAGE); UBTRecycleQueueHeader header = GetRecycleQueueHeader(BufferGetPage(buf), BufferGetBlockNumber(buf)); if (isHead) { + /* buf is new head page */ if (IsNormalOffset(header->head)) { UBTRecycleQueueItem headItem = HeaderGetItem(header, header->head); headItem->prev = InvalidOffset; @@ -487,12 +594,13 @@ void UBTreeRecycleQueuePageChangeEndpointRightPage(Buffer buf, bool isHead) header->head = InvalidOffset; } } else { - /* new created tail page must be empty */ + /* buf is new tail page, newly created tail page must be empty */ Assert(header->head == InvalidOffset); } header->flags |= endpointFlag; } +/* Change end point(head or tail) from buf to newBuf */ static void RecycleQueueChangeEndpoint(Relation rel, Buffer buf, Buffer nextBuf, bool isHead) { Page page = BufferGetPage(buf); @@ -530,6 +638,14 @@ static void RecycleQueueChangeEndpoint(Relation rel, Buffer buf, Buffer nextBuf, END_CRIT_SECTION(); } +/* + * MoveToEndpointPage + * + * Move to the head or tail page of the URQ + * needHead ? head page : tail page + * If we find the head page with headItem->blkno = InvalidBlockNumber, we + * need to switch head. + */ static Buffer MoveToEndpointPage(Relation rel, Buffer buf, bool needHead, int access) { restart: @@ -537,6 +653,7 @@ restart: UBTRecycleQueueHeader header = GetRecycleQueueHeader(page, BufferGetBlockNumber(buf)); uint32 endpointFlag = (needHead ? URQ_HEAD_PAGE : URQ_TAIL_PAGE); + /* Start from the buf that we got from meta page */ while ((header->flags & endpointFlag) == 0) { buf = StepNextPage(rel, buf); page = BufferGetPage(buf); @@ -580,6 +697,13 @@ restart: return buf; } +/* + * PageAllocateItem + * + * Allocate an item offset for a certain UBtree page info + * URQ page will firstly try to allocate offset from freeItems, if not success, + * try to allocate offset from freeListHead. + */ static uint16 PageAllocateItem(Buffer buf) { Page page = BufferGetPage(buf); @@ -604,6 +728,11 @@ static Buffer RecycleQueueExtend(Relation rel) return buf; } +/* + * RecycleQueueLinkNewPage + * + * Link a new URQ page at the end of the queue + */ static void RecycleQueueLinkNewPage(Relation rel, Buffer leftBuf, Buffer newBuf) { /* new page already allocated, link it into the list */ @@ -648,6 +777,7 @@ static void RecycleQueueLinkNewPage(Relation rel, Buffer leftBuf, Buffer newBuf) UnlockReleaseBuffer(rightBuf); } +/* Check whether a certain queue page is empty */ static bool QueuePageIsEmpty(Buffer buf) { UBTRecycleQueueHeader header = GetRecycleQueueHeader(BufferGetPage(buf), BufferGetBlockNumber(buf)); @@ -706,6 +836,14 @@ static void TryFixMetaData(Buffer metaBuf, int32 oldval, int32 newval, bool isHe } } +/* + * RecycleQueueGetEndpointPage + * + * Get the head or the tail page of the URQ + * + * needHead ? head page : tail page + * If the end page has been changed during this function, we need to fix meta data. + */ Buffer RecycleQueueGetEndpointPage(Relation rel, UBTRecycleForkNumber forkNumber, bool needHead, int access) { if (!RecycleQueueInitialized(rel)) { @@ -772,6 +910,13 @@ static void LogModifyPage(Buffer buf, bool isInsert, uint16 offset, UBTRecycleQu PageSetLSN(page, recptr); } +/* + * InsertOnRecycleQueuePage + * + * Insert a UBtree page info into the URQ page + * The item->xid in the URQ page is gradually increasing, so we need to find + * the proper place to insert our item. + */ static void InsertOnRecycleQueuePage(Relation rel, Buffer buf, uint16 offset, BlockNumber blkno, TransactionId xid) { Page page = BufferGetPage(buf); @@ -880,6 +1025,15 @@ void UBTreeXlogRecycleQueueModifyPage(Buffer buf, xl_ubtree2_recycle_queue_modif } } +/* + * RemoveOneItemFromPage + * + * Remove the UBtree page info item from the queue page + * If the item is headitem or tail item of the queue page, just delete it. + * Else, we acquire the prev and next item of it and connect them. Then, + * clear the item, and insert it to freeListHead using the head insertion + * method. If we delete the last item of this page, we need to change head. + */ static void RemoveOneItemFromPage(Relation rel, Buffer buf, uint16 offset) { Page page = BufferGetPage(buf); @@ -936,6 +1090,14 @@ static void RemoveOneItemFromPage(Relation rel, Buffer buf, uint16 offset) } } +/* + * UBTreeRecycleQueueDiscardPage + * + * Discard a certain UBtree page info from URQ + * The parameter addr pointers out the place where the info stores. We get + * the queue page and remove the item. And by the way, we try to remove the + * the invalid items in the left and right side of the item. + */ static void UBTreeRecycleQueueDiscardPage(Relation rel, UBTRecycleQueueAddress addr) { Buffer buf = addr.queueBuf; -- 2.34.1 From cf3037b51298f87d7a401ea788c2d2386fb5e840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=91=E7=9A=84=E6=9C=BA=E9=A1=B6=E7=9B=92?= Date: Wed, 13 Sep 2023 21:42:49 +0800 Subject: [PATCH 2/4] UHeapUpdate commentary. --- .../storage/access/ustore/knl_uheap.cpp | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/gausskernel/storage/access/ustore/knl_uheap.cpp b/src/gausskernel/storage/access/ustore/knl_uheap.cpp index cf3048b26..90b20d624 100644 --- a/src/gausskernel/storage/access/ustore/knl_uheap.cpp +++ b/src/gausskernel/storage/access/ustore/knl_uheap.cpp @@ -2031,12 +2031,26 @@ void PutBlockInplaceUpdateTuple(Page page, Item item, RowPtr *lp, Size size) /* * UHeapUpdate - update a tuple - * - * This function either updates the tuple in-place or it deletes the old - * tuple and new tuple for non-in-place updates. Additionally this function - * inserts an undo record and updates the undo pointer in page header. - * - * For input and output values, see heap_update. + * @param[IN] relation: relation the tuple is in. + * @param[IN] parentRelation: parent relation of the tuple. + * @param[IN] otid: TID of the tuple to update. + * @param[IN] newtup: the new tuple data. + * @param[IN] cid: CID of the updating transaction. + * @param[IN] crosscheck: snapshot to use for checking old tuple's visibility. + * @param[IN] snapshot: snapshot of the current transaction. + * @param[IN] wait: if true, wait for conflicting transactions to end. + * @param[OUT] oldslot: slot to store the old tuple in. + * @param[OUT] tmfd: filled with info about the old tuple. + * @param[OUT] indexkey_update_flag: filled with true if any of the updated columns are index keys. + * @param[OUT] modifiedIdxAttrs: filled with the bitmap of index attributes that are modified. + * @param[IN] allow_inplace_update: if true, allow update in place. + * @return: TM_Result + * @see: heap_update + * @note: This function either updates the tuple in-place or it deletes the old + * tuple and new tuple for non-in-place updates. Additionally this function + * inserts an undo record and updates the undo pointer in page header. +* +* For input and output values, see heap_update. */ TM_Result UHeapUpdate(Relation relation, Relation parentRelation, ItemPointer otid, UHeapTuple newtup, CommandId cid, Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd, @@ -2132,6 +2146,7 @@ TM_Result UHeapUpdate(Relation relation, Relation parentRelation, ItemPointer ot block = ItemPointerGetBlockNumber(otid); page = GetPageBuffer(relation, block, buffer); + // interestingAttrs contains all columns of the index. interestingAttrs = NULL; interestingAttrs = bms_add_members(interestingAttrs, inplaceUpdAttrs); interestingAttrs = bms_add_members(interestingAttrs, keyAttrs); @@ -2140,6 +2155,7 @@ TM_Result UHeapUpdate(Relation relation, Relation parentRelation, ItemPointer ot LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); + // Fetch the tuple to be updated. oldOffnum = ItemPointerGetOffsetNumber(otid); lp = UPageGetRowPtr(page, oldOffnum); Assert(RowPtrIsNormal(lp) || RowPtrIsDeleted(lp)); @@ -2151,6 +2167,11 @@ check_tup_satisfies_update: lockerRemains = false; anyMultiLockerMemberAlive = true; + /* Check whether the tuple is visible to us. + * After the function returned, the oldtup is filled with the tuple data. + * The inplaceUpdated is set to true if the tuple is inplace updated. + * The lockerXid is set to the locker xid if the tuple is locked by other transaction. + */ result = UHeapTupleSatisfiesUpdate(relation, snapshot, otid, &oldtup, cid, buffer, &ctid, &txactinfo, &updateSubXid, &lockerXid, &lockerSubXid, false, multixidIsMyself, &inplaceUpdated); @@ -2197,6 +2218,7 @@ check_tup_satisfies_update: if (!UHeapWait(relation, buffer, &oldtup, lockmode, false, txactinfo.xid, lockerXid, updateSubXid, lockerSubXid, &haveTupleLock, &multixidIsMyself)) { + //we need to recheck the wirte-write conflict goto check_tup_satisfies_update; } @@ -2222,6 +2244,7 @@ check_tup_satisfies_update: * for the same page. */ lp = UPageGetRowPtr(page, oldOffnum); + // if result is not TM_Ok, then we need to fill the tmfd and return the result if (result != TM_Ok) { Assert(result == TM_SelfModified || result == TM_SelfUpdated || result == TM_Updated || result == TM_Deleted || result == TM_BeingModified); @@ -2254,6 +2277,7 @@ check_tup_satisfies_update: bms_free(inplaceUpdAttrs); bms_free(keyAttrs); + // if newtup is not inplace updated or there exists modified attributes, then the indexkey_update_flag is true *indexkey_update_flag = !UHeapTupleIsInPlaceUpdated(((UHeapTuple)newtup)->disk_tuple->flag) || (modifiedIdxAttrs != NULL && *modifiedIdxAttrs != NULL); @@ -2268,6 +2292,9 @@ check_tup_satisfies_update: newtup->table_oid = RelationGetRelid(relation); newtup->xc_node_id = u_sess->pgxc_cxt.PGXCNodeIdentifier; + /* If modifiedAttrs and inplaceUpdAttrs have intersection, then the index attributes are updated. + * In this case, we need to set the modifiedIdxAttrs to the intersection of modifiedAttrs and inplaceUpdAttrs. + */ isIndexUpdated = bms_overlap(modifiedAttrs, inplaceUpdAttrs); if (modifiedIdxAttrs != NULL) { *modifiedIdxAttrs = isIndexUpdated ? bms_intersect(modifiedAttrs, inplaceUpdAttrs) : NULL; @@ -2323,6 +2350,7 @@ check_tup_satisfies_update: } } } else if (newtupsize <= oldtupsize) { + // If newtuple is smaller than oldtuple, then we can do inplace update. useInplaceUpdate = true; } @@ -2463,6 +2491,9 @@ check_tup_satisfies_update: * contention on transaction slots. */ if (!needToast) { + /* Since we can not do inplace update, so we need to find a new page to store the new tuple. + * We try to acquire new page by calling RelationGetBufferForUTuple. + */ newbuf = RelationGetBufferForUTuple(relation, uheaptup->disk_tuple_size, buffer, 0, NULL); } else { /* Re-acquire the lock on the old tuple's page. */ @@ -2614,7 +2645,9 @@ check_tup_satisfies_update: uheaptup = newtup; } - /* Till now, we know whether we will delete the old index */ + /* Till now, we know whether we will delete the old index. + * If the index attributes are updated or the tuple is non-inplace updating, then we will delete the old index tuple. + */ if (oldslot && (*modifiedIdxAttrs != NULL || !useInplaceUpdate)) { *oldslot = MakeSingleTupleTableSlot(relation->rd_att, false, TAM_USTORE); TupleDesc rowDesc = (*oldslot)->tts_tupleDescriptor; @@ -2653,6 +2686,7 @@ check_tup_satisfies_update: uint16 prefixlen = 0; uint16 suffixlen = 0; uint8 xorDeltaFlags = 0; + // oldp and newp point to the data of the tuple, oldlen and newlen are the length of the tuple data. char *oldp = (char *)oldtup.disk_tuple + oldtup.disk_tuple->t_hoff; char *newp = (char *)uheaptup->disk_tuple + uheaptup->disk_tuple->t_hoff; int oldlen = oldtup.disk_tuple_size - oldtup.disk_tuple->t_hoff; @@ -2665,6 +2699,7 @@ check_tup_satisfies_update: oldpTmp = oldp; newpTmp = newp; + // calculate the prefixlen and suffixlen for (prefixlen = 0; prefixlen < minlen; prefixlen++, oldpTmp++, newpTmp++) { if (*oldpTmp != *newpTmp) { break; @@ -2696,7 +2731,7 @@ check_tup_satisfies_update: if (suffixlen > 0) undoXorDeltaSize += sizeof(uint16); } - + /* The undoXorDeltaSize is the length of the delta data stored in the undo record. */ /* The first sizeof(uint8) is space for t_hoff and the second sizeof(uint8) is space for prefix and suffix flag */ undoXorDeltaSize += sizeof(uint8) + oldtup.disk_tuple->t_hoff - OffsetTdId + sizeof(uint8); @@ -2714,6 +2749,7 @@ check_tup_satisfies_update: UndoRecord *undorec = (*u_sess->ustore_cxt.urecvec)[0]; if (useInplaceUpdate) { + // fill the undo record data appendBinaryStringInfo(undorec->Rawdata(), (char *)&(oldtup.disk_tuple->t_hoff), sizeof(uint8)); appendBinaryStringInfo(undorec->Rawdata(), (char *)oldtup.disk_tuple + OffsetTdId, oldtup.disk_tuple->t_hoff - OffsetTdId); -- 2.34.1 From 0ba2494185b203ae7c4b1bb7a0bd270564b92f76 Mon Sep 17 00:00:00 2001 From: zhouzc <522022330108@smail.nju.edu.cn> Date: Fri, 15 Sep 2023 14:49:12 +0800 Subject: [PATCH 3/4] uheapdelete commentary --- .../storage/access/ustore/knl_uheap.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/gausskernel/storage/access/ustore/knl_uheap.cpp b/src/gausskernel/storage/access/ustore/knl_uheap.cpp index 90b20d624..955face7b 100644 --- a/src/gausskernel/storage/access/ustore/knl_uheap.cpp +++ b/src/gausskernel/storage/access/ustore/knl_uheap.cpp @@ -1672,6 +1672,24 @@ bool TableFetchAndStore(Relation scanRelation, Snapshot snapshot, Tuple tuple, B return false; } +/* + * UHeapDelete - delete a heap tuple in the table + * + * Params: + * @param[IN] relation: the uheap relation + * @param[IN] tid: the position of the uheap tuple in the table + * @param[IN] cid: CID of the deleting transaction + * @param[IN] crosscheck: snapshot to use for checking old tuple's visibility. + * @param[IN] snapshot: snapshot of the current transaction. + * @param[IN] wait: if true, wait for conflicting transactions to end. + * @param[OUT] oldslot: slot to store the old tuple in. + * @param[OUT] tmfd: filled with info about the old tuple. + * @param[IN] changingPart: true if we need to move into another partition. + * @param[IN] allowDeleteSelf: use in UHeapTupleSatisfiesUpdate for checking. + * + * For most cases, the returned value is TM_Ok, for we successfully delete a tuple + * form the relation. The old tuple will be placed into te undo zone. + */ TM_Result UHeapDelete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot** oldslot, TM_FailureData *tmfd, bool changingPart, bool allowDeleteSelf) { @@ -1711,10 +1729,12 @@ TM_Result UHeapDelete(Relation relation, ItemPointer tid, CommandId cid, Snapsho RowPtr *rp = UPageGetRowPtr(page, offnum); Assert(RowPtrIsNormal(rp) || RowPtrIsDeleted(rp)); + /* prune FSM and also the page, may delete some utuples */ UHeapPagePruneFSM(relation, buffer, fxid, page, blkno); UHeapResetWaitTimeForTDSlot(); + /* check the status of the utuple */ check_tup_satisfies_update: result = UHeapTupleSatisfiesUpdate(relation, snapshot, tid, &utuple, cid, buffer, &ctid, &tdinfo, &updateSubXid, &lockerXid, &lockerSubXid, false, multixidIsMyself, &inplaceUpdatedOrLocked, allowDeleteSelf); @@ -1798,6 +1818,7 @@ check_tup_satisfies_update: SubXactLockTableInsert(subxid); } + /* reserve a transaction slot on the page */ transSlotId = UHeapPageReserveTransactionSlot(relation, buffer, fxid, &prevUrecptr, &lockReacquired, InvalidBuffer, &minXidInTDSlots); @@ -1882,6 +1903,7 @@ check_tup_satisfies_update: Oid relOid = RelationIsPartition(relation) ? GetBaseRelOidOfParition(relation) : RelationGetRelid(relation); Oid partitionOid = RelationIsPartition(relation) ? RelationGetRelid(relation) : InvalidOid; + /* prepare infomation for undo delete, this include the information of the deleted utuple */ urecptr = UHeapPrepareUndoDelete(relOid, partitionOid, RelationGetRelFileNode(relation), RelationGetRnodeSpace(relation), persistence, buffer, offnum, fxid, subxid, cid, prevUrecptr, INVALID_UNDO_REC_PTR, &oldTD, &utuple, InvalidBlockNumber, NULL, &xlum); -- 2.34.1 From 97f01ca9407bebc24fc4ce2440b47af08ecac596 Mon Sep 17 00:00:00 2001 From: zhouzc <522022330108@smail.nju.edu.cn> Date: Thu, 5 Oct 2023 15:44:04 +0800 Subject: [PATCH 4/4] uheap insert commentary --- .../storage/access/ustore/knl_uheap.cpp | 30 +++++++++++++++---- .../storage/access/ustore/knl_upage.cpp | 7 +++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/gausskernel/storage/access/ustore/knl_uheap.cpp b/src/gausskernel/storage/access/ustore/knl_uheap.cpp index 955face7b..df463a3ad 100644 --- a/src/gausskernel/storage/access/ustore/knl_uheap.cpp +++ b/src/gausskernel/storage/access/ustore/knl_uheap.cpp @@ -489,6 +489,19 @@ static ShortTransactionId UHeapTupleSetModifiedXid(Relation relation, return tupleXid; } +/* + * UHeapInsert - insert a heap tuple in the table + * + * Params: + * @param[IN] rel: the uheap relation + * @param[IN] utuple: the uheap tuple that will be inserted + * @param[IN] cid: CID of the inserting transaction + * @param[IN] bistate: state for bulk inserts. + * @param[IN] isToast: is toast or not. + * + * For most cases, the returned value is InvalidOid, for we successfully insert a tuple + * into the relation table. + */ Oid UHeapInsert(RelationData *rel, UHeapTupleData *utuple, CommandId cid, BulkInsertState bistate, bool isToast) { Page page; @@ -511,9 +524,13 @@ Oid UHeapInsert(RelationData *rel, UHeapTupleData *utuple, CommandId cid, BulkIn ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("The insert tuple is NULL"))); } Assert(utuple->tupTableType == UHEAP_TUPLE); - TransactionId fxid = GetTopTransactionId(); + TransactionId fxid = GetTopTransactionId(); // get the xid of the main transaction - /* Prepare the tuple for insertion */ + /* + * Prepare the tuple for insertion + * Here we should notice that "tuple" and "utuple" point to the same place in memory. + * We fill in some basic information based on the rel into the tuple. + */ tuple = UHeapPrepareInsert(rel, utuple, 0); /* Prepare Undo record before buffer lock since undo record length is fixed */ @@ -603,6 +620,7 @@ reacquire_buffer: /* Put utuple into buffer page */ RelationPutUTuple(rel, buffer, tuple); + // update potential free space of the page UHeapRecordPotentialFreeSpace(buffer, -1 * SHORTALIGN(tuple->disk_tuple_size)); /* Update the UndoRecord now that we know where the tuple is located on the Page */ @@ -747,16 +765,18 @@ TransactionId UHeapFetchInsertXid(UHeapTuple uhtup, Buffer buffer) return result; } - +/* Put utuple into a page */ void RelationPutUTuple(Relation relation, Buffer buffer, UHeapTupleData *tuple) { OffsetNumber offNum = InvalidOffsetNumber; UHeapBufferPage bufpage = {buffer, NULL}; + // UPageAddItem: put the tuple into the buffer page offNum = UPageAddItem(relation, &bufpage, (Item)tuple->disk_tuple, tuple->disk_tuple_size, InvalidOffsetNumber, false); if (offNum == InvalidOffsetNumber) elog(PANIC, "failed to add tuple to page"); + // set item pointer of the tuple->ctid ItemPointerSet(&(tuple->ctid), BufferGetBlockNumber(buffer), offNum); } @@ -767,7 +787,7 @@ UHeapTuple UHeapPrepareInsert(Relation rel, UHeapTupleData *tuple, int options) tuple->disk_tuple->flag &= ~UHEAP_VIS_STATUS_MASK; tuple->disk_tuple->td_id = UHEAPTUP_SLOT_FROZEN; tuple->disk_tuple->locker_td_id = UHEAPTUP_SLOT_FROZEN; - tuple->table_oid = RelationGetRelid(rel); + tuple->table_oid = RelationGetRelid(rel); // Here, get the relid of the relation tuple->t_bucketId = InvalidBktId; if (rel->rd_rel->relkind != RELKIND_RELATION) { @@ -1688,7 +1708,7 @@ bool TableFetchAndStore(Relation scanRelation, Snapshot snapshot, Tuple tuple, B * @param[IN] allowDeleteSelf: use in UHeapTupleSatisfiesUpdate for checking. * * For most cases, the returned value is TM_Ok, for we successfully delete a tuple - * form the relation. The old tuple will be placed into te undo zone. + * from the relation. The old tuple will be placed into te undo zone. */ TM_Result UHeapDelete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot** oldslot, TM_FailureData *tmfd, bool changingPart, bool allowDeleteSelf) diff --git a/src/gausskernel/storage/access/ustore/knl_upage.cpp b/src/gausskernel/storage/access/ustore/knl_upage.cpp index 56f723be7..6cc3ab6b3 100644 --- a/src/gausskernel/storage/access/ustore/knl_upage.cpp +++ b/src/gausskernel/storage/access/ustore/knl_upage.cpp @@ -187,6 +187,11 @@ static void FindNextFreeSlot(const UHeapBufferPage *bufpage, Page input_page, Of } } +/* + * Do the actual insert of the item into the page + * Put the item data on the back or the page and update the page's lower + * and upper pointers. + */ static bool CalculateLowerUpperPointers(Page page, OffsetNumber offsetNumber, Item item, Size size, bool needshuffle) { int lower; @@ -290,6 +295,7 @@ OffsetNumber UPageAddItem(Relation rel, UHeapBufferPage *bufpage, Item item, Siz return InvalidOffsetNumber; } } else { + // Find free slot in the page FindNextFreeSlot(bufpage, input_page, &offsetNumber); } @@ -307,6 +313,7 @@ OffsetNumber UPageAddItem(Relation rel, UHeapBufferPage *bufpage, Item item, Siz return InvalidOffsetNumber; } + // do the actual insert of the item if (!CalculateLowerUpperPointers(page, offsetNumber, item, size, needshuffle)) { return InvalidOffsetNumber; } -- 2.34.1