Beispiel #1
0
  /**
   * 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;
  }
Beispiel #2
0
  /**
   * Add a list of cells to a page. The page should be initially empty. The cells are guaranteed to
   * fit on the page.
   *
   * @param nCell The number of cells to add to this page
   * @param apCell Pointers to cell bodies
   * @param aSize Sizes of the cells
   * @throws SqlJetException
   */
  public void assemblePage(
      int nCell, ISqlJetMemoryPointer[] apCell, int apCellPos, int[] aSize, int aSizePos)
      throws SqlJetException {
    final SqlJetMemPage pPage = this;

    int i; /* Loop counter */
    int hdr = pPage.hdrOffset; /* Index of page header */

    ISqlJetMemoryPointer data = pPage.aData; /* Data for the page */
    int nUsable = pPage.pBt.usableSize;

    assert (pPage.nOverflow == 0);
    assert (pPage.pBt.mutex.held());
    assert (nCell >= 0 && nCell <= pPage.pBt.MX_CELL() && pPage.pBt.MX_CELL() <= 10921);
    assert (pPage.pDbPage.isWriteable());
    assert (pPage.nCell == 0);
    assert (get2byte(data.getMoved(hdr + 5)) == nUsable);

    ISqlJetMemoryPointer pCellPtr = data.getMoved(pPage.cellOffset + nCell * 2);
    int cellbody = nUsable;
    for (i = nCell - 1; i >= 0; i--) {
      int sz = aSize[apCellPos + i];
      pCellPtr = pCellPtr.getMoved(-2);
      cellbody -= sz;
      put2byte(pCellPtr, cellbody);
      memcpy(data.getMoved(cellbody), apCell[apCellPos + i], sz);
    }
    put2byte(data.getMoved(hdr + 3), nCell);
    put2byte(data.getMoved(hdr + 5), cellbody);
    pPage.nFree -= (2 * nCell + pPage.pBt.usableSize - cellbody);
    pPage.nCell = nCell;
  }
Beispiel #3
0
  /**
   * Set up a raw page so that it looks like a database page holding no entries.
   *
   * @param sqlJetBtree
   * @param flags
   * @throws SqlJetException
   */
  void zeroPage(int flags) throws SqlJetException {
    ISqlJetMemoryPointer data = aData;
    byte hdr = hdrOffset;
    int first;

    assert (pDbPage.getPageNumber() == pgno);
    assert (pDbPage.getExtra() == this);
    assert (pDbPage.getData().getBuffer() == data.getBuffer());
    assert (pBt.mutex.held());

    SqlJetUtility.putUnsignedByte(data, hdr, (short) flags);
    first = hdr + 8 + 4 * ((flags & SqlJetMemPage.PTF_LEAF) == 0 ? 1 : 0);
    // SqlJetUtility.memset(data, hdr + 1, (byte) 0, 4);
    SqlJetUtility.put4byte(data, hdr + 1, 0);
    //
    SqlJetUtility.putUnsignedByte(data, hdr + 7, (short) 0);
    SqlJetUtility.put2byte(data, hdr + 5, pBt.usableSize);
    nFree = pBt.usableSize - first;
    decodeFlags(flags);
    hdrOffset = hdr;
    cellOffset = first;
    nOverflow = 0;
    assert (pBt.pageSize >= 512 && pBt.pageSize <= 32768);
    maskPage = pBt.pageSize - 1;
    nCell = 0;
    isInit = true;
  }
Beispiel #4
0
  /**
   * Defragment the page given. All Cells are moved to the end of the page and all free space is
   * collected into one big FreeBlk that occurs in between the header and cell pointer array and the
   * cell content area.
   *
   * @throws SqlJetException
   */
  private void defragmentPage() throws SqlJetException {

    final SqlJetMemPage pPage = this;

    int i; /* Loop counter */
    int pc; /* Address of a i-th cell */
    int hdr; /* Offset to the page header */
    int size; /* Size of a cell */
    int usableSize; /* Number of usable bytes on a page */
    int cellOffset; /* Offset to the cell pointer array */
    int cbrk; /* Offset to the cell content area */
    int nCell; /* Number of cells on the page */
    ISqlJetMemoryPointer data; /* The page data */
    ISqlJetMemoryPointer temp; /* Temp area for cell content */
    int iCellFirst; /* First allowable cell index */
    int iCellLast; /* Last possible cell index */

    assert (pPage.pDbPage.isWriteable());
    assert (pPage.pBt != null);
    assert (pPage.pBt.usableSize <= ISqlJetLimits.SQLJET_MAX_PAGE_SIZE);
    assert (pPage.nOverflow == 0);
    assert (pPage.pBt.mutex.held());
    temp = pPage.pBt.pPager.getTempSpace();
    data = pPage.aData;
    hdr = pPage.hdrOffset;
    cellOffset = pPage.cellOffset;
    nCell = pPage.nCell;
    assert (nCell == get2byte(data, hdr + 3));
    usableSize = pPage.pBt.usableSize;
    cbrk = get2byte(data, hdr + 5);
    memcpy(temp, cbrk, data, cbrk, usableSize - cbrk);
    cbrk = usableSize;
    iCellFirst = cellOffset + 2 * nCell;
    iCellLast = usableSize - 4;
    for (i = 0; i < nCell; i++) {
      final ISqlJetMemoryPointer pAddr =
          data.getBuffer().getPointer(cellOffset + i * 2); /* The i-th cell pointer */
      pc = get2byte(pAddr);
      if (pc < iCellFirst || pc > iCellLast) {
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      assert (pc >= iCellFirst && pc <= iCellLast);
      size = pPage.cellSizePtr(temp.getBuffer().getPointer(pc));
      cbrk -= size;
      if (cbrk < iCellFirst || pc + size > usableSize) {
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      assert (cbrk + size <= usableSize && cbrk >= iCellFirst);
      memcpy(data, cbrk, temp, pc, size);
      put2byte(pAddr, cbrk);
    }
    assert (cbrk >= iCellFirst);
    put2byte(data, hdr + 5, cbrk);
    SqlJetUtility.putUnsignedByte(data, hdr + 1, (byte) 0);
    SqlJetUtility.putUnsignedByte(data, hdr + 2, (byte) 0);
    SqlJetUtility.putUnsignedByte(data, hdr + 7, (byte) 0);
    memset(data, iCellFirst, (byte) 0, cbrk - iCellFirst);
    assert (pPage.pDbPage.isWriteable());
    if (cbrk - iCellFirst != pPage.nFree) {
      throw new SqlJetException(SqlJetErrorCode.CORRUPT);
    }
  }
Beispiel #5
0
  /**
   * Allocate nByte bytes of space on a page.
   *
   * <p>Return the index into pPage->aData[] of the first byte of the new allocation. The caller
   * guarantees that there is enough space. This routine will never fail.
   *
   * <p>If the page contains nBytes of free space but does not contain nBytes of contiguous free
   * space, then this routine automatically calls defragementPage() to consolidate all free space
   * before allocating the new chunk.
   *
   * @param nByte
   * @return
   * @throws SqlJetException
   */
  private int allocateSpace(int nByte) throws SqlJetException {

    final SqlJetMemPage pPage = this;

    int addr, pc, hdr;
    int size;
    int nFrag;
    ISqlJetMemoryPointer data;

    data = pPage.aData;
    assert (pPage.pDbPage.isWriteable());
    assert (pPage.pBt != null);
    assert (pPage.pBt.mutex.held());

    assert (nByte >= 0); /* Minimum cell size is 4 */
    assert (pPage.nFree >= nByte);
    assert (pPage.nOverflow == 0);
    int usableSize = pPage.pBt.usableSize;
    assert (nByte < usableSize - 8);

    hdr = pPage.hdrOffset;

    nFrag = SqlJetUtility.getUnsignedByte(data, hdr + 7);
    assert (pPage.cellOffset == hdr + 12 - 4 * (pPage.leaf ? 1 : 0));
    int gap = pPage.cellOffset + 2 * pPage.nCell;
    int top = get2byte(data.getMoved(hdr + 5));
    if (gap > top) {
      throw new SqlJetException(SqlJetErrorCode.CORRUPT);
    }
    if (nFrag >= 60) {
      pPage.defragmentPage();
      top = get2byte(data.getMoved(hdr + 5));
    } else if (gap + 2 <= top) {
      addr = hdr + 1;
      while ((pc = get2byte(data, addr)) > 0) {
        if (pc > usableSize - 4 || pc < addr + 4) {
          throw new SqlJetException(SqlJetErrorCode.CORRUPT);
        }
        size = get2byte(data, pc + 2);
        if (size >= nByte) {
          int x = size - nByte;
          if (x < 4) {
            memcpy(data, addr, data, pc, 2);
            SqlJetUtility.putUnsignedByte(data, hdr + 7, nFrag + x);
          } else if (size + pc > usableSize) {
            throw new SqlJetException(SqlJetErrorCode.CORRUPT);
          } else {
            put2byte(data, pc + 2, x);
          }
          return pc + x;
        }
        addr = pc;
      }
    }
    if (gap + 2 + nByte > top) {
      pPage.defragmentPage();
      top = get2byte(data, hdr + 5);
      assert (gap + nByte <= top);
    }
    top -= nByte;
    put2byte(data, hdr + 5, top);
    assert (top + nByte <= pPage.pBt.usableSize);
    return top;
  }
Beispiel #6
0
  /**
   * Insert a new cell on pPage at cell index "i". pCell points to the content of the cell.
   *
   * <p>If the cell content will fit on the page, then put it there. If it will not fit, then make a
   * copy of the cell content into pTemp if pTemp is not null. Regardless of pTemp, allocate a new
   * entry in pPage->aOvfl[] and make it point to the cell content (either in pTemp or the original
   * pCell) and also record its index. Allocating a new entry in pPage->aCell[] implies that
   * pPage->nOverflow is incremented.
   *
   * <p>If nSkip is non-zero, then do not copy the first nSkip bytes of the cell. The caller will
   * overwrite them after this function returns. If nSkip is non-zero, then pCell may not point to
   * an invalid memory location (but pCell+nSkip is always valid).
   *
   * @param i New cell becomes the i-th cell of the page
   * @param pCell Content of the new cell
   * @param sz Bytes of content in pCell
   * @param pTemp Temp storage space for pCell, if needed
   * @param nSkip Do not write the first nSkip bytes of the cell
   * @throws SqlJetException
   */
  public void insertCell(
      int i, ISqlJetMemoryPointer pCell, int sz, ISqlJetMemoryPointer pTemp, int iChild)
      throws SqlJetException {

    int nSkip = (iChild > 0 ? 4 : 0);

    final SqlJetMemPage pPage = this;

    int idx; /* Where to write new cell content in data[] */
    int j; /* Loop counter */
    int top; /* First byte of content for any cell in data[] */
    int end; /* First byte past the last cell pointer in data[] */
    int ins; /* Index in data[] where new cell pointer is inserted */
    int hdr; /* Offset into data[] of the page header */
    int cellOffset; /* Address of first cell pointer in data[] */
    ISqlJetMemoryPointer data; /* The content of the whole page */

    assert (i >= 0 && i <= pPage.nCell + pPage.nOverflow);
    assert (pPage.nCell <= pPage.pBt.MX_CELL() && pPage.pBt.MX_CELL() <= 5460);
    assert (pPage.nOverflow <= pPage.aOvfl.length);
    assert (sz == pPage.cellSizePtr(pCell) || (sz == 8 && iChild > 0));
    assert (pPage.pBt.mutex.held());
    if (pPage.nOverflow != 0 || sz + 2 > pPage.nFree) {
      if (pTemp != null) {
        memcpy(pTemp, nSkip, pCell, nSkip, sz - nSkip);
        pCell = pTemp;
      }
      if (iChild > 0) {
        put4byte(pCell, iChild);
      }
      j = pPage.nOverflow++;
      // assert( j<(int)(sizeof(pPage.aOvfl)/sizeof(pPage.aOvfl[0])) );
      pPage.aOvfl[j].pCell = pCell;
      pPage.aOvfl[j].idx = i;
    } else {
      pPage.pDbPage.write();
      assert (pPage.pDbPage.isWriteable());
      data = pPage.aData;
      hdr = pPage.hdrOffset;
      top = get2byte(data, hdr + 5);
      cellOffset = pPage.cellOffset;
      end = cellOffset + 2 * pPage.nCell + 2;
      ins = cellOffset + 2 * i;
      if (end > top - sz) {
        pPage.defragmentPage();
        top = get2byte(data, hdr + 5);
        assert (end + sz <= top);
      }
      idx = pPage.allocateSpace(sz);
      assert (idx > 0);
      assert (end <= get2byte(data, hdr + 5));
      if (idx + sz > pPage.pBt.usableSize) {
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      pPage.nCell++;
      pPage.nFree -= (2 + sz);
      memcpy(data, idx + nSkip, pCell, nSkip, sz - nSkip);
      if (iChild > 0) {
        put4byte(data, idx, iChild);
      }
      for (j = end - 2; j > ins; j -= 2) {
        SqlJetUtility.putUnsignedByte(data, j, SqlJetUtility.getUnsignedByte(data, j - 2));
        SqlJetUtility.putUnsignedByte(data, j + 1, SqlJetUtility.getUnsignedByte(data, j - 1));
      }
      put2byte(data, ins, idx);
      put2byte(data, hdr + 3, pPage.nCell);
      if (pPage.pBt.autoVacuum) {
        /*
         * The cell may contain a pointer to an overflow page. If so,
         * write* the entry for the overflow page into the pointer map.
         */
        SqlJetBtreeCellInfo info = pPage.parseCellPtr(pCell);
        assert ((info.nData + (pPage.intKey ? 0 : info.nKey)) == info.nPayload);
        if ((info.nData + (pPage.intKey ? 0 : info.nKey)) > info.nLocal) {
          int pgnoOvfl = get4byte(pCell, info.iOverflow);
          pPage.pBt.ptrmapPut(pgnoOvfl, SqlJetBtreeShared.PTRMAP_OVERFLOW1, pPage.pgno);
        }
      }
    }
  }
Beispiel #7
0
  /*
   * * Return a section of the pPage->aData to the freelist.* The first byte
   * of the new free block is pPage->aDisk[start]* and the size of the block
   * is "size" bytes.** Most of the effort here is involved in coalesing
   * adjacent* free blocks into a single big free block.
   */
  private void freeSpace(int start, int size) throws SqlJetException {

    SqlJetMemPage pPage = this;

    int addr, pbegin, hdr;
    ISqlJetMemoryPointer data = pPage.aData;

    assert (pPage.pBt != null);
    assert (start >= pPage.hdrOffset + 6 + (pPage.leaf ? 0 : 4));
    assert ((start + size) <= pPage.pBt.usableSize);
    assert (pPage.pBt.mutex.held());
    assert (size >= 0); /* Minimum cell size is 4 */

    if (ISqlJetConfig.SECURE_DELETE) {
      /*
       * Overwrite deleted information with zeros when the SECURE_DELETE*
       * option is enabled at compile-time
       */
      memset(data, start, (byte) 0, size);
    }

    /* Add the space back into the linked list of freeblocks */
    hdr = pPage.hdrOffset;
    addr = hdr + 1;
    while ((pbegin = get2byte(data, addr)) < start && pbegin > 0) {
      assert (pbegin <= pPage.pBt.usableSize - 4);
      if (pbegin <= addr) {
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      addr = pbegin;
    }
    if (pbegin > pPage.pBt.usableSize - 4) {
      throw new SqlJetException(SqlJetErrorCode.CORRUPT);
    }
    assert (pbegin > addr || pbegin == 0);
    put2byte(data, addr, start);
    put2byte(data, start, pbegin);
    put2byte(data, start + 2, size);
    pPage.nFree += size;

    /* Coalesce adjacent free blocks */
    addr = pPage.hdrOffset + 1;
    while ((pbegin = get2byte(data, addr)) > 0) {
      int pnext, psize, x;
      assert (pbegin > addr);
      assert (pbegin <= pPage.pBt.usableSize - 4);
      pnext = get2byte(data, pbegin);
      psize = get2byte(data, pbegin + 2);
      if (pbegin + psize + 3 >= pnext && pnext > 0) {
        int frag = pnext - (pbegin + psize);
        if ((frag < 0) || (frag > (int) SqlJetUtility.getUnsignedByte(data, pPage.hdrOffset + 7))) {
          throw new SqlJetException(SqlJetErrorCode.CORRUPT);
        }
        SqlJetUtility.putUnsignedByte(
            data,
            pPage.hdrOffset + 7,
            (byte) (SqlJetUtility.getUnsignedByte(data, pPage.hdrOffset + 7) - (byte) frag));
        x = get2byte(data, pnext);
        put2byte(data, pbegin, x);
        x = pnext + get2byte(data, pnext + 2) - pbegin;
        put2byte(data, pbegin + 2, x);
      } else {
        addr = pbegin;
      }
    }

    /* If the cell content area begins with a freeblock, remove it. */
    if (SqlJetUtility.getUnsignedByte(data, hdr + 1) == SqlJetUtility.getUnsignedByte(data, hdr + 5)
        && SqlJetUtility.getUnsignedByte(data, hdr + 2)
            == SqlJetUtility.getUnsignedByte(data, hdr + 6)) {
      int top;
      pbegin = get2byte(data, hdr + 1);
      memcpy(data, hdr + 1, data, pbegin, 2);
      top = get2byte(data, hdr + 5) + get2byte(data, pbegin + 2);
      put2byte(data, hdr + 5, top);
    }
    assert (pPage.pDbPage.isWriteable());
  }