/** * Remove the i-th cell from pPage. This routine effects pPage only. The cell content is not freed * or deallocated. It is assumed that the cell content has been copied someplace else. This * routine just removes the reference to the cell from pPage. * * <p>"sz" must be the number of bytes in the cell. * * @param idx * @param sz * @throws SqlJetException */ public void dropCell(int idx, int sz) throws SqlJetException { final SqlJetMemPage pPage = this; int pc; /* Offset to cell content of cell being deleted */ ISqlJetMemoryPointer data; /* pPage->aData */ ISqlJetMemoryPointer ptr; /* Used to move bytes around within data[] */ assert (idx >= 0 && idx < pPage.nCell); assert (sz == pPage.cellSize(idx)); assert (pPage.pBt.mutex.held()); data = pPage.aData; ptr = pointer(data, pPage.cellOffset + 2 * idx); pc = get2byte(ptr); if ((pc < pPage.hdrOffset + 6 + (pPage.leaf ? 0 : 4)) || (pc + sz > pPage.pBt.usableSize)) { throw new SqlJetException(SqlJetErrorCode.CORRUPT); } pPage.freeSpace(pc, sz); final ISqlJetMemoryPointer endPtr = pointer(data, pPage.cellOffset + 2 * pPage.nCell - 2); while (ptr.getPointer() < endPtr.getPointer()) { put2byte(ptr, get2byte(ptr, 2)); movePtr(ptr, 2); } // put2byte(endPtr, 0); pPage.nCell--; put2byte(data, pPage.hdrOffset + 3, pPage.nCell); pPage.nFree += 2; }
/** * Create the byte sequence used to represent a cell on page pPage and write that byte sequence * into pCell[]. Overflow pages are allocated and filled in as necessary. The calling procedure is * responsible for making sure sufficient space has been allocated for pCell[]. * * <p>Note that pCell does not necessary need to point to the pPage->aData area. pCell might point * to some temporary storage. The cell will be constructed in this temporary area then copied into * pPage->aData later. * * @param pCell Complete text of the cell * @param pKey The key * @param nKey The key * @param pData The data * @param nData The data * @param nZero Extra zero bytes to append to pData * @return cell size * @throws SqlJetException */ public int fillInCell( ISqlJetMemoryPointer pCell, ISqlJetMemoryPointer pKey, long nKey, ISqlJetMemoryPointer pData, int nData, int nZero) throws SqlJetException { final SqlJetMemPage pPage = this; int pnSize = 0; int nPayload; ISqlJetMemoryPointer pSrc; int nSrc, n; int spaceLeft; SqlJetMemPage pOvfl = null; SqlJetMemPage pToRelease = null; ISqlJetMemoryPointer pPrior; ISqlJetMemoryPointer pPayload; SqlJetBtreeShared pBt = pPage.pBt; int[] pgnoOvfl = {0}; int nHeader; SqlJetBtreeCellInfo info; assert (pPage.pBt.mutex.held()); /* * pPage is not necessarily writeable since pCell might be auxiliary* * buffer space that is separate from the pPage buffer area */ assert (pCell.getBuffer() != pPage.aData.getBuffer() || pPage.pDbPage.isWriteable()); /* Fill in the header. */ nHeader = 0; if (!pPage.leaf) { nHeader += 4; } if (pPage.hasData) { nHeader += putVarint(pointer(pCell, nHeader), nData + nZero); } else { nData = nZero = 0; } nHeader += putVarint(pointer(pCell, nHeader), nKey); info = pPage.parseCellPtr(pCell); assert (info.nHeader == nHeader); assert (info.nKey == nKey); assert (info.nData == nData + nZero); /* Fill in the payload */ nPayload = nData + nZero; if (pPage.intKey) { pSrc = pData; nSrc = nData; nData = 0; } else { /* TBD: Perhaps raise SQLITE_CORRUPT if nKey is larger than 31 bits? */ nPayload += (int) nKey; pSrc = pKey; nSrc = (int) nKey; } pnSize = info.nSize; spaceLeft = info.nLocal; pPayload = pointer(pCell, nHeader); pPrior = pointer(pCell, info.iOverflow); while (nPayload > 0) { if (spaceLeft == 0) { int pgnoPtrmap = pgnoOvfl[0]; /* * Overflow page pointer-map entry * page */ if (pBt.autoVacuum) { do { pgnoOvfl[0]++; } while (pBt.PTRMAP_ISPAGE(pgnoOvfl[0]) || pgnoOvfl[0] == pBt.PENDING_BYTE_PAGE()); } try { pOvfl = pBt.allocatePage(pgnoOvfl, pgnoOvfl[0], false); /* * If the database supports auto-vacuum, and the second or * subsequent* overflow page is being allocated, add an * entry to the pointer-map* for that page now.** If this is * the first overflow page, then write a partial entry* to * the pointer-map. If we write nothing to this pointer-map * slot,* then the optimistic overflow chain processing in * clearCell()* may misinterpret the uninitialised values * and delete the* wrong pages from the database. */ if (pBt.autoVacuum) { byte eType = (pgnoPtrmap != 0 ? SqlJetBtreeShared.PTRMAP_OVERFLOW2 : SqlJetBtreeShared.PTRMAP_OVERFLOW1); try { pBt.ptrmapPut(pgnoOvfl[0], eType, pgnoPtrmap); } catch (SqlJetException e) { releasePage(pOvfl); } } } catch (SqlJetException e) { releasePage(pToRelease); throw e; } /* * If pToRelease is not zero than pPrior points into the data * area* of pToRelease. Make sure pToRelease is still writeable. */ assert (pToRelease == null || pToRelease.pDbPage.isWriteable()); /* * If pPrior is part of the data area of pPage, then make sure * pPage* is still writeable */ assert (pPrior.getBuffer() != pPage.aData.getBuffer() || pPage.pDbPage.isWriteable()); put4byte(pPrior, pgnoOvfl[0]); releasePage(pToRelease); pToRelease = pOvfl; pPrior = pOvfl.aData; put4byte(pPrior, 0); pPayload = pointer(pOvfl.aData, 4); spaceLeft = pBt.usableSize - 4; } n = nPayload; if (n > spaceLeft) n = spaceLeft; /* * If pToRelease is not zero than pPayload points into the data area * * of pToRelease. Make sure pToRelease is still writeable. */ assert (pToRelease == null || pToRelease.pDbPage.isWriteable()); /* * If pPayload is part of the data area of pPage, then make sure * pPage* is still writeable */ assert (pPayload.getBuffer() != pPage.aData.getBuffer() || pPage.pDbPage.isWriteable()); if (nSrc > 0) { if (n > nSrc) n = nSrc; assert (pSrc != null); memcpy(pPayload, pSrc, n); } else { memset(pPayload, (byte) 0, n); } nPayload -= n; movePtr(pPayload, n); pSrc = pointer(pSrc, n); nSrc -= n; spaceLeft -= n; if (nSrc == 0) { nSrc = nData; pSrc = pData; } } releasePage(pToRelease); return pnSize; }