/* * TUX index contains all tuple versions. A scan in TUX has scanned * one of them and asks if it can be returned as scan result. This * depends on trans id, dirty read flag, and savepoint within trans. * * Previously this faked a ZREAD operation and used getPage(). * In TUP getPage() is run after ACC locking, but TUX comes here * before ACC access. Instead of modifying getPage() it is more * clear to do the full check here. */ bool Dbtup::tuxQueryTh(Uint32 fragPtrI, Uint32 pageId, Uint32 pageIndex, Uint32 tupVersion, Uint32 transId1, Uint32 transId2, bool dirty, Uint32 savepointId) { jamEntry(); FragrecordPtr fragPtr; fragPtr.i= fragPtrI; ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); TablerecPtr tablePtr; tablePtr.i= fragPtr.p->fragTableId; ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); PagePtr pagePtr; pagePtr.i = pageId; c_page_pool.getPtr(pagePtr); KeyReqStruct req_struct(this); { Operationrec tmpOp; tmpOp.m_tuple_location.m_page_no = pageId; tmpOp.m_tuple_location.m_page_idx = pageIndex; tmpOp.op_type = ZREAD; // valgrind setup_fixed_tuple_ref(&req_struct, &tmpOp, tablePtr.p); setup_fixed_part(&req_struct, &tmpOp, tablePtr.p); } Tuple_header* tuple_ptr = req_struct.m_tuple_ptr; OperationrecPtr currOpPtr; currOpPtr.i = tuple_ptr->m_operation_ptr_i; if (currOpPtr.i == RNIL) { jam(); // tuple has no operation, any scan can see it return true; } c_operation_pool.getPtr(currOpPtr); const bool sameTrans = c_lqh->is_same_trans(currOpPtr.p->userpointer, transId1, transId2); bool res = false; OperationrecPtr loopOpPtr = currOpPtr; if (!sameTrans) { jam(); if (!dirty) { jam(); if (currOpPtr.p->nextActiveOp == RNIL) { jam(); // last op - TUX makes ACC lock request in same timeslice res = true; } } else { // loop to first op (returns false) find_savepoint(loopOpPtr, 0); const Uint32 op_type = loopOpPtr.p->op_type; if (op_type != ZINSERT) { jam(); // read committed version const Uint32 origVersion = tuple_ptr->get_tuple_version(); if (origVersion == tupVersion) { jam(); res = true; } } } } else { jam(); // for own trans, ignore dirty flag if (find_savepoint(loopOpPtr, savepointId)) { jam(); const Uint32 op_type = loopOpPtr.p->op_type; if (op_type != ZDELETE) { jam(); // check if this op has produced the scanned version Uint32 loopVersion = loopOpPtr.p->op_struct.bit_field.tupVersion; if (loopVersion == tupVersion) { jam(); res = true; } } } } return res; }
int Dbtup::tuxReadAttrs(EmulatedJamBuffer * jamBuf, Uint32 fragPtrI, Uint32 pageId, Uint32 pageIndex, Uint32 tupVersion, const Uint32* attrIds, Uint32 numAttrs, Uint32* dataOut, bool xfrmFlag) { thrjamEntry(jamBuf); // use own variables instead of globals FragrecordPtr fragPtr; fragPtr.i= fragPtrI; ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); TablerecPtr tablePtr; tablePtr.i= fragPtr.p->fragTableId; ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); // search for tuple version if not original Operationrec tmpOp; KeyReqStruct req_struct(jamBuf); req_struct.tablePtrP = tablePtr.p; req_struct.fragPtrP = fragPtr.p; tmpOp.m_tuple_location.m_page_no= pageId; tmpOp.m_tuple_location.m_page_idx= pageIndex; tmpOp.op_type = ZREAD; // valgrind setup_fixed_tuple_ref(&req_struct, &tmpOp, tablePtr.p); setup_fixed_part(&req_struct, &tmpOp, tablePtr.p); Tuple_header *tuple_ptr= req_struct.m_tuple_ptr; if (tuple_ptr->get_tuple_version() != tupVersion) { jam(); OperationrecPtr opPtr; opPtr.i= tuple_ptr->m_operation_ptr_i; Uint32 loopGuard= 0; while (opPtr.i != RNIL) { c_operation_pool.getPtr(opPtr); if (opPtr.p->op_struct.bit_field.tupVersion == tupVersion) { jam(); if (!opPtr.p->m_copy_tuple_location.isNull()) { req_struct.m_tuple_ptr= get_copy_tuple(&opPtr.p->m_copy_tuple_location); } break; } jam(); opPtr.i= opPtr.p->prevActiveOp; ndbrequire(++loopGuard < (1 << ZTUP_VERSION_BITS)); } } // read key attributes from found tuple version // save globals prepare_read(&req_struct, tablePtr.p, false); // do it int ret = readAttributes(&req_struct, attrIds, numAttrs, dataOut, ZNIL, xfrmFlag); // done return ret; }
int Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageIndex, Uint32* dataOut, bool xfrmFlag) { jamEntry(); // use own variables instead of globals FragrecordPtr fragPtr; fragPtr.i= fragPtrI; ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); TablerecPtr tablePtr; tablePtr.i= fragPtr.p->fragTableId; ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); Operationrec tmpOp; tmpOp.m_tuple_location.m_page_no= pageId; tmpOp.m_tuple_location.m_page_idx= pageIndex; KeyReqStruct req_struct(this); req_struct.tablePtrP = tablePtr.p; req_struct.fragPtrP = fragPtr.p; PagePtr page_ptr; Uint32* ptr= get_ptr(&page_ptr, &tmpOp.m_tuple_location, tablePtr.p); req_struct.m_page_ptr = page_ptr; req_struct.m_tuple_ptr = (Tuple_header*)ptr; int ret = 0; if (! (req_struct.m_tuple_ptr->m_header_bits & Tuple_header::FREE)) { req_struct.check_offset[MM]= tablePtr.p->get_check_offset(MM); req_struct.check_offset[DD]= tablePtr.p->get_check_offset(DD); Uint32 num_attr= tablePtr.p->m_no_of_attributes; Uint32 descr_start= tablePtr.p->tabDescriptor; TableDescriptor *tab_descr= &tableDescriptor[descr_start]; ndbrequire(descr_start + (num_attr << ZAD_LOG_SIZE) <= cnoOfTabDescrRec); req_struct.attr_descr= tab_descr; if(req_struct.m_tuple_ptr->m_header_bits & Tuple_header::ALLOC) { Uint32 opPtrI= req_struct.m_tuple_ptr->m_operation_ptr_i; Operationrec* opPtrP= c_operation_pool.getPtr(opPtrI); ndbassert(!opPtrP->m_copy_tuple_location.isNull()); req_struct.m_tuple_ptr= get_copy_tuple(&opPtrP->m_copy_tuple_location); } prepare_read(&req_struct, tablePtr.p, false); const Uint32* attrIds= &tableDescriptor[tablePtr.p->readKeyArray].tabDescr; const Uint32 numAttrs= tablePtr.p->noOfKeyAttr; // read pk attributes from original tuple // do it ret = readAttributes(&req_struct, attrIds, numAttrs, dataOut, ZNIL, xfrmFlag); // done if (ret >= 0) { // remove headers Uint32 n= 0; Uint32 i= 0; while (n < numAttrs) { const AttributeHeader ah(dataOut[i]); Uint32 size= ah.getDataSize(); ndbrequire(size != 0); for (Uint32 j= 0; j < size; j++) { dataOut[i + j - n]= dataOut[i + j + 1]; } n+= 1; i+= 1 + size; } ndbrequire((int)i == ret); ret -= numAttrs; } else { return ret; } } if (tablePtr.p->m_bits & Tablerec::TR_RowGCI) { dataOut[ret] = *req_struct.m_tuple_ptr->get_mm_gci(tablePtr.p); } else { dataOut[ret] = 0; } return ret; }
/* ----------------------------------------------------------------- */ void Dbtup::execTUP_COMMITREQ(Signal* signal) { FragrecordPtr regFragPtr; OperationrecPtr regOperPtr; TablerecPtr regTabPtr; KeyReqStruct req_struct(this, KRS_COMMIT); TransState trans_state; Uint32 no_of_fragrec, no_of_tablerec; TupCommitReq * const tupCommitReq= (TupCommitReq *)signal->getDataPtr(); regOperPtr.i= tupCommitReq->opPtr; Uint32 hash_value= tupCommitReq->hashValue; Uint32 gci_hi = tupCommitReq->gci_hi; Uint32 gci_lo = tupCommitReq->gci_lo; Uint32 transId1 = tupCommitReq->transId1; Uint32 transId2 = tupCommitReq->transId2; jamEntry(); c_operation_pool.getPtr(regOperPtr); regFragPtr.i= regOperPtr.p->fragmentPtr; trans_state= get_trans_state(regOperPtr.p); no_of_fragrec= cnoOfFragrec; ndbrequire(trans_state == TRANS_STARTED); ptrCheckGuard(regFragPtr, no_of_fragrec, fragrecord); no_of_tablerec= cnoOfTablerec; regTabPtr.i= regFragPtr.p->fragTableId; req_struct.signal= signal; req_struct.hash_value= hash_value; req_struct.gci_hi = gci_hi; req_struct.gci_lo = gci_lo; /* Put transid in req_struct, so detached triggers can access it */ req_struct.trans_id1 = transId1; req_struct.trans_id2 = transId2; req_struct.m_reorg = regOperPtr.p->op_struct.bit_field.m_reorg; regOperPtr.p->m_commit_disk_callback_page = tupCommitReq->diskpage; #ifdef VM_TRACE if (tupCommitReq->diskpage == RNIL) { m_pgman_ptr.i = RNIL; m_pgman_ptr.p = 0; req_struct.m_disk_page_ptr.i = RNIL; req_struct.m_disk_page_ptr.p = 0; } #endif ptrCheckGuard(regTabPtr, no_of_tablerec, tablerec); PagePtr page; Tuple_header* tuple_ptr= (Tuple_header*) get_ptr(&page, ®OperPtr.p->m_tuple_location, regTabPtr.p); /** * NOTE: This has to be run before potential time-slice when * waiting for disk, as otherwise the "other-ops" in a multi-op * commit might run while we're waiting for disk * */ if (!regTabPtr.p->tuxCustomTriggers.isEmpty()) { if(get_tuple_state(regOperPtr.p) == TUPLE_PREPARED) { jam(); OperationrecPtr loopPtr = regOperPtr; if (unlikely(!regOperPtr.p->is_first_operation())) { findFirstOp(loopPtr); } /** * Execute all tux triggers at first commit * since previous tuple is otherwise removed... */ jam(); goto first; while(loopPtr.i != RNIL) { c_operation_pool.getPtr(loopPtr); first: executeTuxCommitTriggers(signal, loopPtr.p, regFragPtr.p, regTabPtr.p); set_tuple_state(loopPtr.p, TUPLE_TO_BE_COMMITTED); loopPtr.i = loopPtr.p->nextActiveOp; } } } bool get_page = false; if(regOperPtr.p->op_struct.bit_field.m_load_diskpage_on_commit) { jam(); Page_cache_client::Request req; /** * Only last op on tuple needs "real" commit, * hence only this one should have m_load_diskpage_on_commit */ ndbassert(tuple_ptr->m_operation_ptr_i == regOperPtr.i); /** * Check for page */ if(!regOperPtr.p->m_copy_tuple_location.isNull()) { jam(); Tuple_header* tmp= get_copy_tuple(®OperPtr.p->m_copy_tuple_location); memcpy(&req.m_page, tmp->get_disk_ref_ptr(regTabPtr.p), sizeof(Local_key)); if (unlikely(regOperPtr.p->op_type == ZDELETE && tmp->m_header_bits & Tuple_header::DISK_ALLOC)) { jam(); /** * Insert+Delete */ regOperPtr.p->op_struct.bit_field.m_load_diskpage_on_commit = 0; regOperPtr.p->op_struct.bit_field.m_wait_log_buffer = 0; disk_page_abort_prealloc(signal, regFragPtr.p, &req.m_page, req.m_page.m_page_idx); D("Logfile_client - execTUP_COMMITREQ"); Logfile_client lgman(this, c_lgman, regFragPtr.p->m_logfile_group_id); lgman.free_log_space(regOperPtr.p->m_undo_buffer_space); goto skip_disk; if (0) ndbout_c("insert+delete"); jamEntry(); goto skip_disk; } } else { jam(); // initial delete ndbassert(regOperPtr.p->op_type == ZDELETE); memcpy(&req.m_page, tuple_ptr->get_disk_ref_ptr(regTabPtr.p), sizeof(Local_key)); ndbassert(tuple_ptr->m_header_bits & Tuple_header::DISK_PART); } if (retrieve_data_page(signal, req, regOperPtr) == 0) { return; // Data page has not been retrieved yet. } get_page = true; } if(regOperPtr.p->op_struct.bit_field.m_wait_log_buffer) { jam(); /** * Only last op on tuple needs "real" commit, * hence only this one should have m_wait_log_buffer */ ndbassert(tuple_ptr->m_operation_ptr_i == regOperPtr.i); if (retrieve_log_page(signal, regFragPtr, regOperPtr) == 0) { return; // Log page has not been retrieved yet. } } assert(tuple_ptr); skip_disk: req_struct.m_tuple_ptr = tuple_ptr; Uint32 nextOp = regOperPtr.p->nextActiveOp; Uint32 prevOp = regOperPtr.p->prevActiveOp; /** * The trigger code (which is shared between detached/imediate) * check op-list to check were to read before values from * detached triggers should always read from original tuple value * from before transaction start, not from any intermediate update * * Setting the op-list has this effect */ regOperPtr.p->nextActiveOp = RNIL; regOperPtr.p->prevActiveOp = RNIL; if(tuple_ptr->m_operation_ptr_i == regOperPtr.i) { jam(); /** * Perform "real" commit */ Uint32 disk = regOperPtr.p->m_commit_disk_callback_page; set_commit_change_mask_info(regTabPtr.p, &req_struct, regOperPtr.p); checkDetachedTriggers(&req_struct, regOperPtr.p, regTabPtr.p, disk != RNIL); tuple_ptr->m_operation_ptr_i = RNIL; if (regOperPtr.p->op_type == ZDELETE) { jam(); if (get_page) { ndbassert(tuple_ptr->m_header_bits & Tuple_header::DISK_PART); } dealloc_tuple(signal, gci_hi, gci_lo, page.p, tuple_ptr, &req_struct, regOperPtr.p, regFragPtr.p, regTabPtr.p); } else if(regOperPtr.p->op_type != ZREFRESH) { jam(); commit_operation(signal, gci_hi, gci_lo, tuple_ptr, page, regOperPtr.p, regFragPtr.p, regTabPtr.p); } else { jam(); commit_refresh(signal, gci_hi, gci_lo, tuple_ptr, page, &req_struct, regOperPtr.p, regFragPtr.p, regTabPtr.p); } } if (nextOp != RNIL) { c_operation_pool.getPtr(nextOp)->prevActiveOp = prevOp; } if (prevOp != RNIL) { c_operation_pool.getPtr(prevOp)->nextActiveOp = nextOp; } if(!regOperPtr.p->m_copy_tuple_location.isNull()) { jam(); c_undo_buffer.free_copy_tuple(®OperPtr.p->m_copy_tuple_location); } initOpConnection(regOperPtr.p); signal->theData[0] = 0; }