// was:btreeMoveto private RC BtreeMoveTo(byte[] key, long nKey, bool biasRight, ref int pRes) { Btree.UnpackedRecord pIdxKey; // Unpacked index key var aSpace = new Btree.UnpackedRecord(); if (key != null) { Debug.Assert(nKey == (long)(int)nKey); pIdxKey = Btree._vdbe.sqlite3VdbeRecordUnpack(this.KeyInfo, (int)nKey, key, aSpace, 16); } else { pIdxKey = null; } var rc = MoveToUnpacked(pIdxKey, nKey, biasRight, ref pRes); if (key != null) { Btree._vdbe.sqlite3VdbeDeleteUnpackedRecord(pIdxKey); } return(rc); }
// was:sqlite3BtreeMovetoUnpacked public RC MoveToUnpacked(Btree.UnpackedRecord idxKey, long intKey, bool biasRight, ref int pRes) { Debug.Assert(HoldsMutex()); Debug.Assert(MutexEx.Held(Tree.DB.Mutex)); Debug.Assert((idxKey == null) == (KeyInfo == null)); // If the cursor is already positioned at the point we are trying to move to, then just return without doing any work if (State == CursorState.VALID && ValidNKey && Pages[0].HasIntKey) { if (Info.nKey == intKey) { pRes = 0; return(RC.OK); } if (AtLast && Info.nKey < intKey) { pRes = -1; return(RC.OK); } } var rc = MoveToRoot(); if (rc != RC.OK) { return(rc); } Debug.Assert(Pages[PageID] != null); Debug.Assert(Pages[PageID].HasInit); Debug.Assert(Pages[PageID].Cells > 0 || State == CursorState.INVALID); if (State == CursorState.INVALID) { pRes = -1; Debug.Assert(Pages[PageID].Cells == 0); return(RC.OK); } Debug.Assert(Pages[0].HasIntKey || idxKey != null); for (; ;) { var page = Pages[PageID]; // pPage.nCell must be greater than zero. If this is the root-page the cursor would have been INVALID above and this for(;;) loop // not run. If this is not the root-page, then the moveToChild() routine would have already detected db corruption. Similarly, pPage must // be the right kind (index or table) of b-tree page. Otherwise a moveToChild() or moveToRoot() call would have detected corruption. Debug.Assert(page.Cells > 0); Debug.Assert(page.HasIntKey == (idxKey == null)); var lwr = 0; var upr = page.Cells - 1; int idx; PagesIndexs[PageID] = (ushort)(biasRight ? (idx = upr) : (idx = (upr + lwr) / 2)); int c; for (; ;) { Debug.Assert(idx == PagesIndexs[PageID]); Info.nSize = 0; var cell = page.FindCell(idx) + page.ChildPtrSize; // Pointer to current cell in pPage if (page.HasIntKey) { var nCellKey = 0L; if (page.HasData != 0) { uint dummy0; cell += ConvertEx.GetVariant4(page.Data, (uint)cell, out dummy0); } ConvertEx.GetVariant9L(page.Data, (uint)cell, out nCellKey); if (nCellKey == intKey) { c = 0; } else if (nCellKey < intKey) { c = -1; } else { Debug.Assert(nCellKey > intKey); c = 1; } ValidNKey = true; Info.nKey = nCellKey; } else { // The maximum supported page-size is 65536 bytes. This means that the maximum number of record bytes stored on an index B-Tree // page is less than 16384 bytes and may be stored as a 2-byte varint. This information is used to attempt to avoid parsing // the entire cell by checking for the cases where the record is stored entirely within the b-tree page by inspecting the first // 2 bytes of the cell. var nCell = (int)page.Data[cell + 0]; if (0 == (nCell & 0x80) && nCell <= page.MaxLocal) { // This branch runs if the record-size field of the cell is a single byte varint and the record fits entirely on the main b-tree page. c = Btree._vdbe.sqlite3VdbeRecordCompare(nCell, page.Data, cell + 1, idxKey); } else if (0 == (page.Data[cell + 1] & 0x80) && (nCell = ((nCell & 0x7f) << 7) + page.Data[cell + 1]) <= page.MaxLocal) { // The record-size field is a 2 byte varint and the record fits entirely on the main b-tree page. c = Btree._vdbe.sqlite3VdbeRecordCompare(nCell, page.Data, cell + 2, idxKey); } else { // The record flows over onto one or more overflow pages. In this case the whole cell needs to be parsed, a buffer allocated // and accessPayload() used to retrieve the record into the buffer before VdbeRecordCompare() can be called. var pCellBody = new byte[page.Data.Length - cell + page.ChildPtrSize]; Buffer.BlockCopy(page.Data, cell - page.ChildPtrSize, pCellBody, 0, pCellBody.Length); page.btreeParseCellPtr(pCellBody, ref Info); nCell = (int)Info.nKey; var pCellKey = MallocEx.sqlite3Malloc(nCell); rc = AccessPayload(0, (uint)nCell, pCellKey, false); if (rc != RC.OK) { pCellKey = null; goto moveto_finish; } c = Btree._vdbe.sqlite3VdbeRecordCompare(nCell, pCellKey, idxKey); pCellKey = null; } } if (c == 0) { if (page.HasIntKey && 0 == page.Leaf) { lwr = idx; upr = lwr - 1; break; } else { pRes = 0; rc = RC.OK; goto moveto_finish; } } if (c < 0) { lwr = idx + 1; } else { upr = idx - 1; } if (lwr > upr) { break; } PagesIndexs[PageID] = (ushort)(idx = (lwr + upr) / 2); } Debug.Assert(lwr == upr + 1); Debug.Assert(page.HasInit); Pgno chldPg; if (page.Leaf != 0) { chldPg = 0; } else if (lwr >= page.Cells) { chldPg = ConvertEx.Get4(page.Data, page.HeaderOffset + 8); } else { chldPg = ConvertEx.Get4(page.Data, page.FindCell(lwr)); } if (chldPg == 0) { Debug.Assert(PagesIndexs[PageID] < Pages[PageID].Cells); pRes = c; rc = RC.OK; goto moveto_finish; } PagesIndexs[PageID] = (ushort)lwr; Info.nSize = 0; ValidNKey = false; rc = MoveToChild(chldPg); if (rc != RC.OK) { goto moveto_finish; } } moveto_finish: return(rc); }