Package at.jku.sii.sqlitereader

Source Code of at.jku.sii.sqlitereader.SqliteDataBase

/*$Id$*/
package at.jku.sii.sqlitereader;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import at.jku.sii.sqlitereader.annotator.SimpleAnnotator;
import at.jku.sii.sqlitereader.btree.BTreePage;
import at.jku.sii.sqlitereader.btree.BTreePages;
import at.jku.sii.sqlitereader.btree.TableCell;
import at.jku.sii.sqlitereader.io.ArrayDataInput;
import at.jku.sii.sqlitereader.io.FileDataInput;
import at.jku.sii.sqlitereader.model.SQLiteObject;
import at.jku.sii.sqlitereader.model.master.SqliteMasterTable;
import at.jku.sii.sqlitereader.page.FreeListPage;
import at.jku.sii.sqlitereader.page.LockBytePage;
import at.jku.sii.sqlitereader.page.Page;
import at.jku.sii.sqlitereader.page.PointerMapPage;
import at.jku.sii.sqlitereader.page.RawDataPage;

public final class SqliteDataBase implements Dumpable {
  public enum FileFormat {
    LEGACY, WAL
  }

  public static final long HEADER_SIZE = 100;

  private final String fileName;

  private final FileFormat fileFormatWrite;
  private final FileFormat fileFormatRead;

  private final int pageSize;
  private final int numPages;

  private final int fileChangeCounter;

  private final DataBaseEncoding encoding;

  private final boolean incrementalVacuumMode;

  private final int userVersion;
  private final int versionValid4Number;
  private final int versionSqlite;

  private final List<Page> pages;

  private int firstFreeListPage;
  private int numFreeListPages;
  private final List<FreeListPage> freePages;

  private final byte reservedPageSpace;

  private final int largestBTreePage;

  private final List<PointerMapPage> pointerMapPages;
  private final LockBytePage lockBytePage;

  private int schemaCookie;
  private int schemaFormat;

  private int defaultPageCacheSize;

  private final SqliteMasterTable sql_master_table;

  private final SimpleAnnotator annotator;

  public SqliteDataBase(File dbFile) {
    FileDataInput file = null;
    this.annotator = new SimpleAnnotator();

    try {
      file = new FileDataInput(dbFile, this.annotator);

      this.fileName = dbFile.getAbsolutePath();

      file.annotate(0, (int) HEADER_SIZE, "Header");
      // start of header
      readMagicHeader(file);
      this.pageSize = readPageSize(file);
      this.fileFormatWrite = file.readByte() == 1 ? FileFormat.LEGACY : FileFormat.WAL;
      file.annotateLastByte("writeFileFormat", this.fileFormatWrite);
      this.fileFormatRead = file.readByte() == 1 ? FileFormat.LEGACY : FileFormat.WAL;
      file.annotateLastByte("readFileFormat", this.fileFormatRead);

      this.reservedPageSpace = file.readByte("reservedPageSpace");

      byte maxEmbedded = file.readByte("maxEmbedded");
      assert (maxEmbedded == 64);
      byte minEmbedded = file.readByte("minEmbedded");
      assert (minEmbedded == 32);
      byte leafPayload = file.readByte("leafPayload");
      assert (leafPayload == 32);
      this.fileChangeCounter = file.readInt("fileChangeCounter");
      int storedNumPages = file.readInt("storedNumPages");

      this.firstFreeListPage = file.readInt("firstFreeListPage");
      this.numFreeListPages = file.readInt("numFreeListPages");

      this.schemaCookie = file.readInt("schemaCookie");
      this.schemaFormat = file.readInt("schemaForamt"); // one of 1..4
      this.defaultPageCacheSize = file.readInt("defaultPageCacheSize");

      this.largestBTreePage = file.readInt("largestBTreePage");

      this.encoding = readEncoding(file);
      file.annotateLastInt("Encoding", this.encoding);

      this.userVersion = file.readInt("userVersion");
      this.incrementalVacuumMode = file.readInt() > 0;
      file.annotateLastInt("incrementalVacuumMode", this.incrementalVacuumMode);
      // reserved
      byte[] reserved = new byte[24];
      file.readFully(reserved);
      file.annotateLast(reserved.length, "reserved");

      this.versionValid4Number = file.readInt("versionValid4Number");
      this.versionSqlite = file.readInt("versionSqlite");
      // end of header

      // compute the num of Pages
      if (isValidNumPages(storedNumPages, this.fileChangeCounter, this.versionValid4Number)) {
        this.numPages = storedNumPages;
      } else {
        this.numPages = file.size() / this.pageSize;
      }

      // read all pages
      this.pages = readPages(file, this.numPages, this.pageSize);
    } finally {
      if (file != null)
        file.close();
      file = null;
    }

    // read root btree page
    BTreePage<TableCell> sql_master_btree = BTreePages.readMaster(this);
    assert (sql_master_btree.isTable()); // must be a table
    sql_master_btree.resolve(this);

    // read pointer map pages
    this.pointerMapPages = PointerMapPage.readPointerMapPages(this);

    // read free pages
    this.freePages = FreeListPage.readPages(this, this.firstFreeListPage, this.numFreeListPages);

    // read lock byte page
    this.lockBytePage = LockBytePage.read(this);

    this.sql_master_table = new SqliteMasterTable(sql_master_btree);
    this.sql_master_table.resolve(this);
  }

  private static List<Page> readPages(FileDataInput file, int numPages, int pageSize) {
    List<Page> pages = new ArrayList<Page>(numPages);
    file.seek(0); // seek to start
    for (int i = 0; i < numPages; ++i) {
      byte[] data = new byte[pageSize];
      file.readFully(data);
      // file.annotateLast(data.length, "Page", i + 1);
      ArrayDataInput din = new ArrayDataInput(data, file.createLastSubAnnotator(data.length));
      pages.add(new RawDataPage(i, din));
    }
    return pages;
  }

  private static DataBaseEncoding readEncoding(FileDataInput file) {
    int encoding = file.readInt();
    switch (encoding) {
    case 1:
      return DataBaseEncoding.UTF8;
    case 2:
      return DataBaseEncoding.UTF16LE;
    case 3:
      return DataBaseEncoding.UTF16BE;
    default:
      throw new IllegalStateException();
    }
  }

  private static boolean isValidNumPages(int storedNumPages, int fileChangeCounter, int versionValid4Number) {
    return storedNumPages > 0 && (fileChangeCounter == versionValid4Number);
  }

  public ArrayDataInput getPageBlock(int pageNum) {
    pageNum--; // starting with 1
    assert (pageNum < this.numPages);
    Page page = this.pages.get(pageNum);
    assert (page instanceof RawDataPage); // not yet resolved
    return ((RawDataPage) page).getContent();
  }

  public void resolvePage(int pageNum, Page specific) {
    pageNum--; // starting with 1
    assert (pageNum < this.numPages);
    final Page ori = this.pages.get(pageNum);
    assert (ori instanceof RawDataPage); // not yet resolved
    ((RawDataPage) ori).getContent().annotate(0, this.pageSize, "Page", specific.getClass().getSimpleName());
    this.pages.set(pageNum, specific);
  }

  public int getUsableSize() {
    return this.pageSize - this.reservedPageSpace;
  }

  private static void readMagicHeader(FileDataInput file) {
    byte[] b = new byte[16];
    file.readFully(b);
    file.annotateLast(b.length, "Magic Header", "SQLite format 3");
    byte[] expected = { 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00 };
    for (int i = 0; i < 16; ++i)
      assert (b[i] == expected[i]);
    // System.out.println(Arrays.toString(b));
  }

  private static int readPageSize(FileDataInput file) {
    int s = file.readShort();
    if (s == 1)
      s = 65536;
    file.annotateLastShort("PageSize", s);
    return s;
  }

  public SqliteMasterTable getSql_master_table() {
    return this.sql_master_table;
  }

  public int getLargestBTreePage() {
    return this.largestBTreePage;
  }

  public Iterable<FreeListPage> getFreePages() {
    return this.freePages;
  }

  public Iterable<PointerMapPage> getPointerMapPages() {
    return this.pointerMapPages;
  }

  public LockBytePage getLockBytePage() {
    return this.lockBytePage;
  }

  public byte getReservedPageSpace() {
    return this.reservedPageSpace;
  }

  public String getFileName() {
    return this.fileName;
  }

  public FileFormat getFileFormatWrite() {
    return this.fileFormatWrite;
  }

  public int getPageSize() {
    return this.pageSize;
  }

  public FileFormat getFileFormatRead() {
    return this.fileFormatRead;
  }

  public int getFileChangeCounter() {
    return this.fileChangeCounter;
  }

  public int getNumPages() {
    return this.numPages;
  }

  public DataBaseEncoding getEncoding() {
    return this.encoding;
  }

  public boolean isIncrementalVacuumMode() {
    return this.incrementalVacuumMode;
  }

  public int getUserVersion() {
    return this.userVersion;
  }

  public int getVersionValid4Number() {
    return this.versionValid4Number;
  }

  public int getVersionSqlite() {
    return this.versionSqlite;
  }

  public int getDefaultPageCacheSize() {
    return this.defaultPageCacheSize;
  }

  public int getSchemaCookie() {
    return this.schemaCookie;
  }

  public int getSchemaFormat() {
    return this.schemaFormat;
  }

  public SimpleAnnotator getAnnotator() {
    return this.annotator;
  }

  @Override
  public void dump(StringBuilder builder) {
    builder.append("<h1>SqlliteDataBase</h1>");
    builder.append("\n<h2>General:</h2>\n");
    {
      builder.append("<pre>");
      builder.append(" fileName\t\t= ").append(this.fileName).append("\n fileFormatWrite\t= ").append(this.fileFormatWrite)
          .append("\n fileFormatRead\t\t= ")
          .append(this.fileFormatRead);
      builder.append("\n fileChangeCounter\t= ").append(this.fileChangeCounter).append("\n encoding\t\t= ").append(this.encoding)
          .append("\n incrementalVacuumMode\t= ").append(this.incrementalVacuumMode).append("\n userVersion\t\t= ").append(this.userVersion)
          .append("\n versionValid4Number\t= ").append(this.versionValid4Number).append("\n versionSqlite\t\t= ").append(this.versionSqlite);
      builder.append("\n reservedPageSpace\t= ").append(this.reservedPageSpace).append("\n largestBTreePage\t= ").append(this.largestBTreePage);
      builder.append("\n schemaCookie\t\t= ").append(this.schemaCookie).append("\n schemaFormat\t\t= ").append(this.schemaFormat);
      builder.append("\n defaultPageCacheSize\t= ").append(this.defaultPageCacheSize);
      builder.append("</pre>");

    }
    builder.append("\n\n<h2>Free List:</h2>\n");
    {
      builder.append("<pre>");
      builder.append(" firstFreeListPage\t= ").append(this.firstFreeListPage).append("\n numFreeListPages\t= ").append(this.numFreeListPages)
          .append("\n");
      builder.append("</pre>");
      DumpUtils.join(this.freePages, builder, "\n");
    }
    builder.append("\n\n<h2>Pointer Map Pages:</h2>\n");
    {
      DumpUtils.join(this.pointerMapPages, builder, "\n");
    }
    builder.append("\n\n<h2>Lock Byte Page:</h2>\n");
    {
      if (this.lockBytePage != null)
        this.lockBytePage.dump(builder);
      else
        builder.append("none");
    }
    builder.append("\n\n<h2>Pages:</h2>\n");
    {
      builder.append("<pre>");
      builder.append(" pageSize\t= ").append(this.pageSize).append("\n numPages\t= ").append(this.numPages).append('\n');
      builder.append("</pre>");
      DumpUtils.join(this.pages, builder, "\n\n");
    }
    builder.append("\n\n");
    this.sql_master_table.dump(builder);

    builder.append("\n\n<h2>Schema:</h2>\n");
    for (SQLiteObject obj : this.sql_master_table.objects())
      obj.dump(builder);

    // builder.append("\n\n");
    // this.annotator.dump(builder);
  }

  public static void main(String[] args) {
    final File dbFile = new File(args[0]);
    SqliteDataBase db = new SqliteDataBase(dbFile);

    HTMLAnnotationRenderer.render(db, dbFile);

    System.out.println(DumpUtils.dump(db));
  }
}
TOP

Related Classes of at.jku.sii.sqlitereader.SqliteDataBase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.