Ejemplo n.º 1
0
  /**
   * Set the pointer-map entries for all children of page pPage. Also, if pPage contains cells that
   * point to overflow pages, set the pointer map entries for the overflow pages as well.
   *
   * @throws SqlJetException
   */
  public void setChildPtrmaps() throws SqlJetException {
    int i; /* Counter variable */
    int nCell; /* Number of cells in page pPage */

    boolean isInitOrig = isInit;

    assert (pBt.mutex.held());
    try {
      initPage();
      nCell = this.nCell;

      for (i = 0; i < nCell; i++) {
        ISqlJetMemoryPointer pCell = findCell(i);

        ptrmapPutOvflPtr(pCell);

        if (!leaf) {
          int childPgno = get4byte(pCell);
          pBt.ptrmapPut(childPgno, SqlJetBtreeShared.PTRMAP_BTREE, pgno);
        }
      }

      if (!leaf) {
        int childPgno = get4byte(aData, hdrOffset + 8);
        pBt.ptrmapPut(childPgno, SqlJetBtreeShared.PTRMAP_BTREE, pgno);
      }
    } catch (SqlJetException e) {
      // set_child_ptrmaps_out:
      isInit = isInitOrig;
      throw e;
    }
  }
Ejemplo n.º 2
0
  /** * Free any overflow pages associated with the given Cell. */
  public void clearCell(ISqlJetMemoryPointer pCell) throws SqlJetException {
    SqlJetBtreeCellInfo info;
    int[] ovflPgno = new int[1];
    int nOvfl;
    int ovflPageSize;

    assert (pBt.mutex.held());
    info = parseCellPtr(pCell);
    if (info.iOverflow == 0) {
      return; /* No overflow pages. Return without doing anything */
    }
    ovflPgno[0] = get4byte(pCell, info.iOverflow);
    ovflPageSize = pBt.usableSize - 4;
    nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1) / ovflPageSize;
    assert (ovflPgno[0] == 0 || nOvfl > 0);
    while (nOvfl-- != 0) {
      SqlJetMemPage[] pOvfl = new SqlJetMemPage[1];
      if (ovflPgno[0] < 2 || ovflPgno[0] > pBt.pPager.getPageCount()) {
        /* 0 is not a legal page number and page 1 cannot be an
         ** overflow page. Therefore if ovflPgno<2 or past the end of the
         ** file the database must be corrupt. */
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }

      pBt.getOverflowPage(ovflPgno[0], pOvfl, (nOvfl == 0) ? null : ovflPgno);
      pOvfl[0].freePage();
      pOvfl[0].pDbPage.unref();
    }
  }
Ejemplo n.º 3
0
 /**
  * If the cell pCell, part of page pPage contains a pointer to an overflow page, insert an entry
  * into the pointer-map for the overflow page.
  *
  * @throws SqlJetException
  */
 public void ptrmapPutOvflPtr(ISqlJetMemoryPointer pCell) throws SqlJetException {
   assert (pCell != null);
   SqlJetBtreeCellInfo info = parseCellPtr(pCell);
   assert ((info.nData + (intKey ? 0 : info.nKey)) == info.nPayload);
   if ((info.nData + (intKey ? 0 : info.nKey)) > info.nLocal) {
     int ovfl = get4byte(pCell, info.iOverflow);
     pBt.ptrmapPut(ovfl, SqlJetBtreeShared.PTRMAP_OVERFLOW1, pgno);
   }
 }
Ejemplo n.º 4
0
  /**
   * Initialize the auxiliary information for a disk block.
   *
   * <p>Return SQLITE_OK on success. If we see that the page does not contain a well-formed database
   * page, then return SQLITE_CORRUPT. Note that a return of SQLITE_OK does not guarantee that the
   * page is well-formed. It only shows that we failed to detect any corruption.
   */
  public void initPage() throws SqlJetException {

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

    if (!isInit) {
      int pc; /* Address of a freeblock within pPage->aData[] */
      byte hdr; /* Offset to beginning of page header */
      int usableSize; /* Amount of usable space on each page */
      int cellOffset; /* Offset from start of page to first cell pointer */
      int nFree; /* Number of unused bytes on the page */
      int top; /* First byte of the cell content area */

      hdr = hdrOffset;
      decodeFlags(SqlJetUtility.getUnsignedByte(aData, hdr));
      assert (pBt.pageSize >= 512 && pBt.pageSize <= 32768);
      maskPage = pBt.pageSize - 1;
      nOverflow = 0;
      usableSize = pBt.usableSize;
      this.cellOffset = cellOffset = hdr + 12 - 4 * (leaf ? 1 : 0);
      top = get2byte(aData, hdr + 5);
      nCell = get2byte(aData, hdr + 3);
      if (nCell > pBt.MX_CELL()) {
        /* To many cells for a single page. The page must be corrupt */
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      int iCellFirst = cellOffset + 2 * this.nCell;

      /* Compute the total free space on the page */
      pc = get2byte(aData, hdr + 1);
      nFree = SqlJetUtility.getUnsignedByte(aData, hdr + 7) + top; // - (cellOffset + 2 * nCell);
      while (pc > 0) {
        int next, size;
        if (pc > usableSize - 4) {
          /* Free block is off the page */
          throw new SqlJetException(SqlJetErrorCode.CORRUPT);
        }
        next = get2byte(aData, pc);
        size = get2byte(aData, pc + 2);
        if (next > 0 && next <= pc + size + 3) {
          /* Free blocks must be in accending order */
          throw new SqlJetException(SqlJetErrorCode.CORRUPT);
        }
        nFree += size;
        pc = next;
      }
      if (nFree > usableSize) {
        /* Free space cannot exceed total page size */
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      }
      this.nFree = (nFree - iCellFirst);
      isInit = true;
    }
  }
Ejemplo n.º 5
0
  /** Add a page of the database file to the freelist. unref() is NOT called for pPage. */
  public void freePage() throws SqlJetException {
    SqlJetMemPage pPage1 = pBt.pPage1;
    int n, k;

    /* Prepare the page for freeing */
    assert (pBt.mutex.held());
    assert (this.pgno > 1);
    this.isInit = false;

    /* Increment the free page count on pPage1 */
    pPage1.pDbPage.write();
    n = get4byte(pPage1.aData, 36);
    put4byte(pPage1.aData, 36, n + 1);

    if (ISqlJetConfig.SECURE_DELETE) {
      /*
       * If the SQLITE_SECURE_DELETE compile-time option is enabled, then
       * always fully overwrite deleted information with zeros.
       */
      pDbPage.write();
      memset(aData, (byte) 0, pBt.pageSize);
    }

    /*
     * If the database supports auto-vacuum, write an entry in the
     * pointer-map to indicate that the page is free.
     */
    if (ISAUTOVACUUM()) {
      pBt.ptrmapPut(pgno, SqlJetBtreeShared.PTRMAP_FREEPAGE, 0);
    }

    if (n == 0) {
      /* This is the first free page */
      pDbPage.write();
      memset(aData, (byte) 0, 8);
      put4byte(pPage1.aData, 32, pgno);
      TRACE("FREE-PAGE: %d first\n", this.pgno);
    } else {
      /*
       * Other free pages already exist. Retrive the first trunk page* of
       * the freelist and find out how many leaves it has.
       */
      SqlJetMemPage pTrunk;
      pTrunk = pBt.getPage(get4byte(pPage1.aData, 32), false);
      k = get4byte(pTrunk.aData, 4);
      if (k >= pBt.usableSize / 4 - 8) {
        /*
         * The trunk is full. Turn the page being freed into a new*
         * trunk page with no leaves.** Note that the trunk page is not
         * really full until it contains* usableSize/4 - 2 entries, not
         * usableSize/4 - 8 entries as we have* coded. But due to a
         * coding error in versions of SQLite prior to* 3.6.0, databases
         * with freelist trunk pages holding more than* usableSize/4 - 8
         * entries will be reported as corrupt. In order* to maintain
         * backwards compatibility with older versions of SQLite,* we
         * will contain to restrict the number of entries to
         * usableSize/4 - 8* for now. At some point in the future (once
         * everyone has upgraded* to 3.6.0 or later) we should consider
         * fixing the conditional above* to read "usableSize/4-2"
         * instead of "usableSize/4-8".
         */
        pDbPage.write();
        put4byte(aData, pTrunk.pgno);
        put4byte(aData, 4, 0);
        put4byte(pPage1.aData, 32, pgno);
        TRACE("FREE-PAGE: %d new trunk page replacing %d\n", this.pgno, pTrunk.pgno);
      } else if (k < 0) {
        throw new SqlJetException(SqlJetErrorCode.CORRUPT);
      } else {
        /* Add the newly freed page as a leaf on the current trunk */
        pTrunk.pDbPage.write();
        put4byte(pTrunk.aData, 4, k + 1);
        put4byte(pTrunk.aData, 8 + k * 4, pgno);
        if (ISqlJetConfig.SECURE_DELETE) {
          pDbPage.dontWrite();
        }
        TRACE("FREE-PAGE: %d leaf on trunk page %d\n", this.pgno, pTrunk.pgno);
      }
      releasePage(pTrunk);
    }
  }
Ejemplo n.º 6
0
  /**
   * 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;
  }