public IEnumerable <Sqlite3Row> EnumerateRows() { IEnumerable <BTreeCellData> cells = BTreeTools.WalkTableBTree(RootPage); List <ColumnDataMeta> metaInfos = new List <ColumnDataMeta>(); foreach (BTreeCellData cell in cells) { metaInfos.Clear(); // Create a new stream to cover any fragmentation that might occur // The stream is started in the current cells "resident" data, and will overflow to any other pages as needed using (SqliteDataStream dataStream = new SqliteDataStream(_reader, cell.Page, (ushort)(cell.CellOffset + cell.Cell.CellHeaderSize), cell.Cell.DataSizeInCell, cell.Cell.FirstOverflowPage, cell.Cell.DataSize)) { ReaderBase reader = new ReaderBase(dataStream, _reader); long headerSize = reader.ReadVarInt(out _); while (reader.Position < headerSize) { long columnInfo = reader.ReadVarInt(out _); ColumnDataMeta meta = new ColumnDataMeta(); if (columnInfo == 0) { meta.Type = SqliteDataType.Null; } else if (columnInfo == 1) { meta.Type = SqliteDataType.Integer; meta.Length = 1; } else if (columnInfo == 2) { meta.Type = SqliteDataType.Integer; meta.Length = 2; } else if (columnInfo == 3) { meta.Type = SqliteDataType.Integer; meta.Length = 3; } else if (columnInfo == 4) { meta.Type = SqliteDataType.Integer; meta.Length = 4; } else if (columnInfo == 5) { meta.Type = SqliteDataType.Integer; meta.Length = 6; } else if (columnInfo == 6) { meta.Type = SqliteDataType.Integer; meta.Length = 8; } else if (columnInfo == 7) { meta.Type = SqliteDataType.Float; meta.Length = 8; } else if (columnInfo == 8) { meta.Type = SqliteDataType.Boolean0; } else if (columnInfo == 9) { meta.Type = SqliteDataType.Boolean1; } else if (columnInfo == 10 || columnInfo == 11) { throw new ArgumentOutOfRangeException(); } else if ((columnInfo & 0x01) == 0x00) { // Even number meta.Type = SqliteDataType.Blob; meta.Length = (uint)((columnInfo - 12) / 2); } else { // Odd number meta.Type = SqliteDataType.Text; meta.Length = (uint)((columnInfo - 13) / 2); } metaInfos.Add(meta); } object[] rowData = new object[metaInfos.Count]; for (int i = 0; i < metaInfos.Count; i++) { ColumnDataMeta meta = metaInfos[i]; switch (meta.Type) { case SqliteDataType.Null: rowData[i] = null; break; case SqliteDataType.Integer: // TODO: Do we handle negatives correctly? rowData[i] = reader.ReadInteger((byte)meta.Length); break; case SqliteDataType.Float: rowData[i] = BitConverter.Int64BitsToDouble(reader.ReadInteger((byte)meta.Length)); break; case SqliteDataType.Boolean0: rowData[i] = false; break; case SqliteDataType.Boolean1: rowData[i] = true; break; case SqliteDataType.Blob: rowData[i] = reader.Read((int)meta.Length); break; case SqliteDataType.Text: rowData[i] = reader.ReadString(meta.Length); break; default: throw new ArgumentOutOfRangeException(); } } yield return(new Sqlite3Row(this, cell.Cell.RowId, rowData)); } } }