manageiq-smartstate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +4 -0
- data/.rspec_ci +4 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +202 -0
- data/README.md +45 -0
- data/Rakefile +23 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/MiqContainerGroup/MiqContainerGroup.rb +31 -0
- data/lib/MiqVm/MiqLocalVm.rb +50 -0
- data/lib/MiqVm/MiqRhevmVm.rb +179 -0
- data/lib/MiqVm/MiqVm.rb +355 -0
- data/lib/MiqVm/miq_azure_vm.rb +96 -0
- data/lib/MiqVm/miq_scvmm_vm.rb +38 -0
- data/lib/MiqVm/test/camcorder_fleece_test.rb +60 -0
- data/lib/MiqVm/test/localVm.rb +45 -0
- data/lib/MiqVm/test/partitionAlignmentCheck.rb +76 -0
- data/lib/MiqVm/test/remoteVm.rb +65 -0
- data/lib/MiqVm/test/rhevmNfsTest.rb +62 -0
- data/lib/MiqVm/test/rhevmNfsTest2.rb +66 -0
- data/lib/MiqVm/test/rhevmTest.rb +70 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackCommon.rb +107 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackImage.rb +67 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackInstance.rb +182 -0
- data/lib/Scvmm/miq_hyperv_disk.rb +273 -0
- data/lib/Scvmm/miq_scvmm_parse_powershell.rb +75 -0
- data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +135 -0
- data/lib/Scvmm/test/miq_hyperv_disk_test.rb +33 -0
- data/lib/Scvmm/test/miq_scvmm_vm_ssa_info_test.rb +41 -0
- data/lib/VmLocalDiskAccess/test/localCfg.rb +97 -0
- data/lib/VolumeManager/LVM/logical_volume.rb +75 -0
- data/lib/VolumeManager/LVM/lv_segment.rb +43 -0
- data/lib/VolumeManager/LVM/lvm2disk.rb +158 -0
- data/lib/VolumeManager/LVM/parser.rb +138 -0
- data/lib/VolumeManager/LVM/physical_volume.rb +19 -0
- data/lib/VolumeManager/LVM/scanner.rb +156 -0
- data/lib/VolumeManager/LVM/thin/btree.rb +83 -0
- data/lib/VolumeManager/LVM/thin/constants.rb +86 -0
- data/lib/VolumeManager/LVM/thin/data_map.rb +44 -0
- data/lib/VolumeManager/LVM/thin/mapping_tree.rb +19 -0
- data/lib/VolumeManager/LVM/thin/space_maps.rb +58 -0
- data/lib/VolumeManager/LVM/thin/superblock.rb +136 -0
- data/lib/VolumeManager/LVM/thin.rb +6 -0
- data/lib/VolumeManager/LVM/volume_group.rb +97 -0
- data/lib/VolumeManager/LVM.rb +8 -0
- data/lib/VolumeManager/MiqLdm.rb +546 -0
- data/lib/VolumeManager/MiqLvm.rb +17 -0
- data/lib/VolumeManager/MiqNativeVolumeManager.rb +150 -0
- data/lib/VolumeManager/MiqVolumeManager.rb +277 -0
- data/lib/VolumeManager/VolMgrPlatformSupport.rb +18 -0
- data/lib/VolumeManager/VolMgrPlatformSupportLinux.rb +77 -0
- data/lib/VolumeManager/VolMgrPlatformSupportWin.rb +17 -0
- data/lib/VolumeManager/test/blockDevTest.rb +40 -0
- data/lib/VolumeManager/test/ldm.rb +97 -0
- data/lib/blackbox/VmBlackBox.rb +103 -0
- data/lib/blackbox/xmlStorage.rb +180 -0
- data/lib/db/MiqBdb/MiqBdb.rb +309 -0
- data/lib/db/MiqBdb/MiqBdbBtree.rb +219 -0
- data/lib/db/MiqBdb/MiqBdbHash.rb +199 -0
- data/lib/db/MiqBdb/MiqBdbPage.rb +159 -0
- data/lib/db/MiqBdb/MiqBdbUtil.rb +18 -0
- data/lib/db/MiqSqlite/MiqSqlite3.rb +330 -0
- data/lib/db/MiqSqlite/MiqSqlite3Cell.rb +167 -0
- data/lib/db/MiqSqlite/MiqSqlite3Page.rb +151 -0
- data/lib/db/MiqSqlite/MiqSqlite3Table.rb +124 -0
- data/lib/db/MiqSqlite/MiqSqlite3Util.rb +32 -0
- data/lib/disk/DiskProbe.rb +68 -0
- data/lib/disk/MiqDisk.rb +317 -0
- data/lib/disk/camcorder_test.rb +90 -0
- data/lib/disk/dos_mbr.img +0 -0
- data/lib/disk/modules/AzureBlobDisk.rb +101 -0
- data/lib/disk/modules/LocalDevMod.rb +47 -0
- data/lib/disk/modules/LocalDevProbe.rb +6 -0
- data/lib/disk/modules/MSCommon.rb +352 -0
- data/lib/disk/modules/MSVSDiffDisk.rb +91 -0
- data/lib/disk/modules/MSVSDiskProbe.rb +61 -0
- data/lib/disk/modules/MSVSDynamicDisk.rb +42 -0
- data/lib/disk/modules/MSVSFixedDisk.rb +45 -0
- data/lib/disk/modules/MiqLargeFile.rb +63 -0
- data/lib/disk/modules/MiqLargeFileWin32.rb +107 -0
- data/lib/disk/modules/QcowDisk.rb +692 -0
- data/lib/disk/modules/QcowDiskProbe.rb +34 -0
- data/lib/disk/modules/RawBlockIO.rb +116 -0
- data/lib/disk/modules/RawDisk.rb +45 -0
- data/lib/disk/modules/RawDiskProbe.rb +7 -0
- data/lib/disk/modules/RhevmDescriptor.rb +167 -0
- data/lib/disk/modules/RhevmDiskProbe.rb +52 -0
- data/lib/disk/modules/VMWareCowdDisk.rb +207 -0
- data/lib/disk/modules/VMWareDescriptor.rb +214 -0
- data/lib/disk/modules/VMWareDiskProbe.rb +74 -0
- data/lib/disk/modules/VMWareSparseDisk.rb +189 -0
- data/lib/disk/modules/VhdxDisk.rb +625 -0
- data/lib/disk/modules/VhdxDiskProbe.rb +46 -0
- data/lib/disk/modules/VixDiskMod.rb +54 -0
- data/lib/disk/modules/VixDiskProbe.rb +6 -0
- data/lib/disk/modules/miq_disk_cache.rb +135 -0
- data/lib/disk/modules/miq_dummy_disk.rb +41 -0
- data/lib/disk/modules/vhdx_bat_entry.rb +10 -0
- data/lib/disk/test.rb +66 -0
- data/lib/fs/MetakitFS/MetakitFS.rb +530 -0
- data/lib/fs/MetakitFS/test/Makefile +14 -0
- data/lib/fs/MetakitFS/test/MkCollectFiles.rb +165 -0
- data/lib/fs/MetakitFS/test/MkSelectFiles.rb +30 -0
- data/lib/fs/MetakitFS/test/collect_files.yaml +70 -0
- data/lib/fs/MetakitFS/test/init.rb +3 -0
- data/lib/fs/MetakitFS/test/mk2vmdk.rb +64 -0
- data/lib/fs/MetakitFS/test/mk4test.c +92 -0
- data/lib/fs/MetakitFS/test/mkFsTest.rb +113 -0
- data/lib/fs/MetakitFS/test/proto.rb +97 -0
- data/lib/fs/MiqFS/FsProbe.rb +39 -0
- data/lib/fs/MiqFS/MiqFS.rb +515 -0
- data/lib/fs/MiqFS/modules/AUFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/Ext3.rb +305 -0
- data/lib/fs/MiqFS/modules/Ext3Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Ext4.rb +304 -0
- data/lib/fs/MiqFS/modules/Ext4Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Fat32.rb +318 -0
- data/lib/fs/MiqFS/modules/Fat32Probe.rb +30 -0
- data/lib/fs/MiqFS/modules/HFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/Iso9660.rb +293 -0
- data/lib/fs/MiqFS/modules/Iso9660Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/LocalFS.rb +105 -0
- data/lib/fs/MiqFS/modules/NTFS.rb +287 -0
- data/lib/fs/MiqFS/modules/NTFSProbe.rb +21 -0
- data/lib/fs/MiqFS/modules/NativeFS.rb +155 -0
- data/lib/fs/MiqFS/modules/ReFSProbe.rb +17 -0
- data/lib/fs/MiqFS/modules/RealFS.rb +79 -0
- data/lib/fs/MiqFS/modules/RealFSProbe.rb +6 -0
- data/lib/fs/MiqFS/modules/Reiser4Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/ReiserFS.rb +315 -0
- data/lib/fs/MiqFS/modules/ReiserFSProbe.rb +42 -0
- data/lib/fs/MiqFS/modules/UnionFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/WebDAV.rb +127 -0
- data/lib/fs/MiqFS/modules/WebDAVFile.rb +68 -0
- data/lib/fs/MiqFS/modules/XFS.rb +300 -0
- data/lib/fs/MiqFS/modules/XFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/ZFSProbe.rb +18 -0
- data/lib/fs/MiqFS/test.rb +59 -0
- data/lib/fs/MiqFsUtil.rb +383 -0
- data/lib/fs/MiqMountManager.rb +209 -0
- data/lib/fs/MiqNativeMountManager.rb +101 -0
- data/lib/fs/MountManagerProbe.rb +29 -0
- data/lib/fs/ReiserFS/block.rb +209 -0
- data/lib/fs/ReiserFS/directory.rb +136 -0
- data/lib/fs/ReiserFS/directory_entry.rb +140 -0
- data/lib/fs/ReiserFS/file_data.rb +111 -0
- data/lib/fs/ReiserFS/superblock.rb +140 -0
- data/lib/fs/ReiserFS/utils.rb +95 -0
- data/lib/fs/VimDatastoreFS/VimDatastoreFS.rb +192 -0
- data/lib/fs/ext3/alloc_bitmap.rb +38 -0
- data/lib/fs/ext3/block_pointers_path.rb +130 -0
- data/lib/fs/ext3/directory.rb +51 -0
- data/lib/fs/ext3/directory_entry.rb +67 -0
- data/lib/fs/ext3/ex_attrib_header.rb +14 -0
- data/lib/fs/ext3/ex_attrib_name.rb +23 -0
- data/lib/fs/ext3/file_data.rb +130 -0
- data/lib/fs/ext3/group_descriptor_entry.rb +65 -0
- data/lib/fs/ext3/group_descriptor_table.rb +54 -0
- data/lib/fs/ext3/hash_tree_entry.rb +18 -0
- data/lib/fs/ext3/hash_tree_header.rb +15 -0
- data/lib/fs/ext3/inode.rb +228 -0
- data/lib/fs/ext3/posix_acl_entry.rb +29 -0
- data/lib/fs/ext3/posix_acl_header.rb +11 -0
- data/lib/fs/ext3/superblock.rb +406 -0
- data/lib/fs/ext3/test/tc_Ext3BlockPointersPath.rb +74 -0
- data/lib/fs/ext4/alloc_bitmap.rb +38 -0
- data/lib/fs/ext4/directory.rb +87 -0
- data/lib/fs/ext4/directory_entry.rb +77 -0
- data/lib/fs/ext4/ex_attrib_header.rb +14 -0
- data/lib/fs/ext4/ex_attrib_name.rb +23 -0
- data/lib/fs/ext4/extent.rb +35 -0
- data/lib/fs/ext4/extent_header.rb +40 -0
- data/lib/fs/ext4/extent_index.rb +33 -0
- data/lib/fs/ext4/group_descriptor_entry.rb +69 -0
- data/lib/fs/ext4/group_descriptor_table.rb +54 -0
- data/lib/fs/ext4/hash_tree_entry.rb +58 -0
- data/lib/fs/ext4/hash_tree_header.rb +35 -0
- data/lib/fs/ext4/inode.rb +465 -0
- data/lib/fs/ext4/posix_acl_entry.rb +29 -0
- data/lib/fs/ext4/posix_acl_header.rb +11 -0
- data/lib/fs/ext4/superblock.rb +412 -0
- data/lib/fs/fat32/boot_sect.rb +379 -0
- data/lib/fs/fat32/directory.rb +222 -0
- data/lib/fs/fat32/directory_entry.rb +540 -0
- data/lib/fs/fat32/file_data.rb +128 -0
- data/lib/fs/iso9660/boot_sector.rb +170 -0
- data/lib/fs/iso9660/directory.rb +90 -0
- data/lib/fs/iso9660/directory_entry.rb +147 -0
- data/lib/fs/iso9660/file_data.rb +78 -0
- data/lib/fs/iso9660/rock_ridge.rb +329 -0
- data/lib/fs/iso9660/util.rb +57 -0
- data/lib/fs/modules/LinuxMount.rb +300 -0
- data/lib/fs/modules/LinuxMountProbe.rb +29 -0
- data/lib/fs/modules/WinMount.rb +97 -0
- data/lib/fs/modules/WinMountProbe.rb +24 -0
- data/lib/fs/ntfs/attrib_attribute_list.rb +131 -0
- data/lib/fs/ntfs/attrib_bitmap.rb +26 -0
- data/lib/fs/ntfs/attrib_data.rb +74 -0
- data/lib/fs/ntfs/attrib_file_name.rb +110 -0
- data/lib/fs/ntfs/attrib_header.rb +194 -0
- data/lib/fs/ntfs/attrib_index_allocation.rb +19 -0
- data/lib/fs/ntfs/attrib_index_root.rb +247 -0
- data/lib/fs/ntfs/attrib_object_id.rb +40 -0
- data/lib/fs/ntfs/attrib_standard_information.rb +107 -0
- data/lib/fs/ntfs/attrib_type.rb +49 -0
- data/lib/fs/ntfs/attrib_volume_information.rb +53 -0
- data/lib/fs/ntfs/attrib_volume_name.rb +31 -0
- data/lib/fs/ntfs/boot_sect.rb +253 -0
- data/lib/fs/ntfs/data_run.rb +358 -0
- data/lib/fs/ntfs/directory_index_node.rb +114 -0
- data/lib/fs/ntfs/index_node_header.rb +69 -0
- data/lib/fs/ntfs/index_record_header.rb +85 -0
- data/lib/fs/ntfs/mft_entry.rb +288 -0
- data/lib/fs/ntfs/utils.rb +43 -0
- data/lib/fs/test/camcorder_fs_test.rb +108 -0
- data/lib/fs/test/collect_files_direct.yaml +22 -0
- data/lib/fs/test/collect_files_in.yaml +24 -0
- data/lib/fs/test/collect_files_in_nc.yaml +22 -0
- data/lib/fs/test/collect_files_out.yaml +6 -0
- data/lib/fs/test/collect_files_rm.yaml +6 -0
- data/lib/fs/test/copyTest.rb +126 -0
- data/lib/fs/test/fsTest.rb +87 -0
- data/lib/fs/test/updateTest.rb +184 -0
- data/lib/fs/xfs/allocation_group.rb +160 -0
- data/lib/fs/xfs/bmap_btree_block.rb +125 -0
- data/lib/fs/xfs/bmap_btree_record.rb +80 -0
- data/lib/fs/xfs/bmap_btree_root_node.rb +72 -0
- data/lib/fs/xfs/directory.rb +133 -0
- data/lib/fs/xfs/directory2_data_header.rb +27 -0
- data/lib/fs/xfs/directory3_data_header.rb +34 -0
- data/lib/fs/xfs/directory_block_tail.rb +22 -0
- data/lib/fs/xfs/directory_data_header.rb +46 -0
- data/lib/fs/xfs/directory_entry.rb +106 -0
- data/lib/fs/xfs/inode.rb +532 -0
- data/lib/fs/xfs/inode_map.rb +100 -0
- data/lib/fs/xfs/short_form_directory_entry.rb +91 -0
- data/lib/fs/xfs/short_form_header.rb +44 -0
- data/lib/fs/xfs/superblock.rb +556 -0
- data/lib/lib/tasks/azure.rake +52 -0
- data/lib/manageiq/smartstate/version.rb +5 -0
- data/lib/manageiq/smartstate.rb +7 -0
- data/lib/manageiq-smartstate.rb +1 -0
- data/lib/metadata/MIQExtract/MIQExtract.rb +297 -0
- data/lib/metadata/MIQExtract/test/extractTest.rb +41 -0
- data/lib/metadata/MIQExtract/test/full_extract_test.rb +68 -0
- data/lib/metadata/ScanProfile/HostScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfiles.rb +41 -0
- data/lib/metadata/ScanProfile/ScanItemBase.rb +63 -0
- data/lib/metadata/ScanProfile/ScanProfileBase.rb +51 -0
- data/lib/metadata/ScanProfile/ScanProfilesBase.rb +60 -0
- data/lib/metadata/ScanProfile/VmScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfiles.rb +38 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemFile.rb +51 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemNteventlog.rb +84 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemFile.rb +39 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemNteventlog.rb +34 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemRegistry.rb +64 -0
- data/lib/metadata/VMMount/VMMount.rb +81 -0
- data/lib/metadata/VMMount/VMPlatformMount.rb +18 -0
- data/lib/metadata/VMMount/VMPlatformMountLinux.rb +75 -0
- data/lib/metadata/VMMount/VMPlatformMountWin.rb +13 -0
- data/lib/metadata/VmConfig/GetNativeCfg.rb +45 -0
- data/lib/metadata/VmConfig/VmConfig.rb +947 -0
- data/lib/metadata/VmConfig/cfgConfig.rb +45 -0
- data/lib/metadata/VmConfig/ovfConfig.rb +99 -0
- data/lib/metadata/VmConfig/test/GetVMwareCfgTest.rb +40 -0
- data/lib/metadata/VmConfig/vmcConfig.rb +116 -0
- data/lib/metadata/VmConfig/vmtxConfig.rb +4 -0
- data/lib/metadata/VmConfig/vmxConfig.rb +162 -0
- data/lib/metadata/VmConfig/xmlConfig.rb +79 -0
- data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +41 -0
- data/lib/metadata/linux/InitProcHash.rb +632 -0
- data/lib/metadata/linux/LinuxInitProcs.rb +142 -0
- data/lib/metadata/linux/LinuxOSInfo.rb +237 -0
- data/lib/metadata/linux/LinuxPackages.rb +209 -0
- data/lib/metadata/linux/LinuxSystemd.rb +130 -0
- data/lib/metadata/linux/LinuxUsers.rb +289 -0
- data/lib/metadata/linux/LinuxUtils.rb +197 -0
- data/lib/metadata/linux/MiqConaryPackages.rb +41 -0
- data/lib/metadata/linux/MiqRpmPackages.rb +160 -0
- data/lib/metadata/linux/test/Name +0 -0
- data/lib/metadata/linux/test/Packages +0 -0
- data/lib/metadata/linux/test/rpoTest.rb +5 -0
- data/lib/metadata/linux/test/tc_LinuxUtils.rb +4157 -0
- data/lib/metadata/util/event_log_filter.rb +61 -0
- data/lib/metadata/util/md5deep.rb +280 -0
- data/lib/metadata/util/win32/Win32Accounts.rb +764 -0
- data/lib/metadata/util/win32/Win32EventLog.rb +743 -0
- data/lib/metadata/util/win32/Win32Services.rb +86 -0
- data/lib/metadata/util/win32/Win32Software.rb +326 -0
- data/lib/metadata/util/win32/Win32System.rb +333 -0
- data/lib/metadata/util/win32/boot_info_win.rb +59 -0
- data/lib/metadata/util/win32/fleece_hives.rb +220 -0
- data/lib/metadata/util/win32/ms-registry.rb +650 -0
- data/lib/metadata/util/win32/peheader.rb +868 -0
- data/lib/metadata/util/win32/remote-registry.rb +142 -0
- data/lib/metadata/util/win32/system_path_win.rb +103 -0
- data/lib/metadata/util/win32/versioninfo.rb +17 -0
- data/manageiq-smartstate.gemspec +35 -0
- metadata +486 -0
@@ -0,0 +1,330 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
############################################################################################
|
4
|
+
#
|
5
|
+
# SQLite implements an external (disk-based) database using BTrees.
|
6
|
+
# For a detailed discussion of BTrees, refer to
|
7
|
+
#
|
8
|
+
# Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
|
9
|
+
# "Sorting And Searching", pages 473-480. Addison-Wesley
|
10
|
+
# Publishing Company, Reading, Massachusetts.
|
11
|
+
#
|
12
|
+
# The basic idea is that each page of the file contains N database
|
13
|
+
# entries and N+1 pointers to subpages.
|
14
|
+
#
|
15
|
+
# ----------------------------------------------------------------
|
16
|
+
# | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
|
17
|
+
# ----------------------------------------------------------------
|
18
|
+
#
|
19
|
+
# All of the keys on the page that Ptr(0) points to have values less
|
20
|
+
# than Key(0). All of the keys on page Ptr(1) and its subpages have
|
21
|
+
# values greater than Key(0) and less than Key(1). All of the keys
|
22
|
+
# on Ptr(N) and its subpages have values greater than Key(N-1). And
|
23
|
+
# so forth.
|
24
|
+
#
|
25
|
+
# Finding a particular key requires reading O(log(M)) pages from the
|
26
|
+
# disk where M is the number of entries in the tree.
|
27
|
+
#
|
28
|
+
# In this implementation, a single file can hold one or more separate
|
29
|
+
# BTrees. Each BTree is identified by the index of its root page. The
|
30
|
+
# key and data for any entry are combined to form the "payload". A
|
31
|
+
# fixed amount of payload can be carried directly on the database
|
32
|
+
# page. If the payload is larger than the preset amount then surplus
|
33
|
+
# bytes are stored on overflow pages. The payload for an entry
|
34
|
+
# and the preceding pointer are combined to form a "Cell". Each
|
35
|
+
# page has a small header which contains the Ptr(N) pointer and other
|
36
|
+
# information such as the size of key and data.
|
37
|
+
#
|
38
|
+
# FORMAT DETAILS
|
39
|
+
#
|
40
|
+
# The file is divided into pages. The first page is called page 1,
|
41
|
+
# the second is page 2, and so forth. A page number of zero indicates
|
42
|
+
# "no such page". The page size can be anything between 512 and 65536.
|
43
|
+
# Each page can be either a btree page, a freelist page or an overflow
|
44
|
+
# page.
|
45
|
+
#
|
46
|
+
# The first page is always a btree page. The first 100 bytes of the first
|
47
|
+
# page contain a special header (the "file header") that describes the file.
|
48
|
+
# The format of the file header is as follows:
|
49
|
+
#
|
50
|
+
# OFFSET SIZE DESCRIPTION
|
51
|
+
# 0 16 Header string: "SQLite format 3\000"
|
52
|
+
# 16 2 Page size in bytes.
|
53
|
+
# 18 1 File format write version
|
54
|
+
# 19 1 File format read version
|
55
|
+
# 20 1 Bytes of unused space at the end of each page
|
56
|
+
# 21 1 Max embedded payload fraction
|
57
|
+
# 22 1 Min embedded payload fraction
|
58
|
+
# 23 1 Min leaf payload fraction
|
59
|
+
# 24 4 File change counter
|
60
|
+
# 28 4 Reserved for future use
|
61
|
+
# 32 4 First freelist page
|
62
|
+
# 36 4 Number of freelist pages in the file
|
63
|
+
# 40 60 15 4-byte meta values passed to higher layers
|
64
|
+
#
|
65
|
+
# All of the integer values are big-endian (most significant byte first).
|
66
|
+
#
|
67
|
+
# The file change counter is incremented when the database is changed
|
68
|
+
# This counter allows other processes to know when the file has changed
|
69
|
+
# and thus when they need to flush their cache.
|
70
|
+
#
|
71
|
+
# The max embedded payload fraction is the amount of the total usable
|
72
|
+
# space in a page that can be consumed by a single cell for standard
|
73
|
+
# B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default
|
74
|
+
# is to limit the maximum cell size so that at least 4 cells will fit
|
75
|
+
# on one page. Thus the default max embedded payload fraction is 64.
|
76
|
+
#
|
77
|
+
# If the payload for a cell is larger than the max payload, then extra
|
78
|
+
# payload is spilled to overflow pages. Once an overflow page is allocated,
|
79
|
+
# as many bytes as possible are moved into the overflow pages without letting
|
80
|
+
# the cell size drop below the min embedded payload fraction.
|
81
|
+
#
|
82
|
+
# The min leaf payload fraction is like the min embedded payload fraction
|
83
|
+
# except that it applies to leaf nodes in a LEAFDATA tree. The maximum
|
84
|
+
# payload fraction for a LEAFDATA tree is always 100% (or 255) and it
|
85
|
+
# not specified in the header.
|
86
|
+
#
|
87
|
+
# Each btree pages is divided into three sections: The header, the
|
88
|
+
# cell pointer array, and the cell content area. Page 1 also has a 100-byte
|
89
|
+
# file header that occurs before the page header.
|
90
|
+
#
|
91
|
+
# |----------------|
|
92
|
+
# | file header | 100 bytes. Page 1 only.
|
93
|
+
# |----------------|
|
94
|
+
# | page header | 8 bytes for leaves. 12 bytes for interior nodes
|
95
|
+
# |----------------|
|
96
|
+
# | cell pointer | | 2 bytes per cell. Sorted order.
|
97
|
+
# | array | | Grows downward
|
98
|
+
# | | v
|
99
|
+
# |----------------|
|
100
|
+
# | unallocated |
|
101
|
+
# | space |
|
102
|
+
# |----------------| ^ Grows upwards
|
103
|
+
# | cell content | | Arbitrary order interspersed with freeblocks.
|
104
|
+
# | area | | and free space fragments.
|
105
|
+
# |----------------|
|
106
|
+
#
|
107
|
+
# The page headers looks like this:
|
108
|
+
#
|
109
|
+
# OFFSET SIZE DESCRIPTION
|
110
|
+
# 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
|
111
|
+
# 1 2 byte offset to the first freeblock
|
112
|
+
# 3 2 number of cells on this page
|
113
|
+
# 5 2 first byte of the cell content area
|
114
|
+
# 7 1 number of fragmented free bytes
|
115
|
+
# 8 4 Right child (the Ptr(N) value). Omitted on leaves.
|
116
|
+
#
|
117
|
+
# The flags define the format of this btree page. The leaf flag means that
|
118
|
+
# this page has no children. The zerodata flag means that this page carries
|
119
|
+
# only keys and no data. The intkey flag means that the key is a integer
|
120
|
+
# which is stored in the key size entry of the cell header rather than in
|
121
|
+
# the payload area.
|
122
|
+
#
|
123
|
+
# The cell pointer array begins on the first byte after the page header.
|
124
|
+
# The cell pointer array contains zero or more 2-byte numbers which are
|
125
|
+
# offsets from the beginning of the page to the cell content in the cell
|
126
|
+
# content area. The cell pointers occur in sorted order. The system strives
|
127
|
+
# to keep free space after the last cell pointer so that new cells can
|
128
|
+
# be easily added without having to defragment the page.
|
129
|
+
#
|
130
|
+
# Cell content is stored at the very end of the page and grows toward the
|
131
|
+
# beginning of the page.
|
132
|
+
#
|
133
|
+
# Unused space within the cell content area is collected into a linked list of
|
134
|
+
# freeblocks. Each freeblock is at least 4 bytes in size. The byte offset
|
135
|
+
# to the first freeblock is given in the header. Freeblocks occur in
|
136
|
+
# increasing order. Because a freeblock must be at least 4 bytes in size,
|
137
|
+
# any group of 3 or fewer unused bytes in the cell content area cannot
|
138
|
+
# exist on the freeblock chain. A group of 3 or fewer free bytes is called
|
139
|
+
# a fragment. The total number of bytes in all fragments is recorded.
|
140
|
+
# in the page header at offset 7.
|
141
|
+
#
|
142
|
+
# SIZE DESCRIPTION
|
143
|
+
# 2 Byte offset of the next freeblock
|
144
|
+
# 2 Bytes in this freeblock
|
145
|
+
#
|
146
|
+
# Cells are of variable length. Cells are stored in the cell content area at
|
147
|
+
# the end of the page. Pointers to the cells are in the cell pointer array
|
148
|
+
# that immediately follows the page header. Cells is not necessarily
|
149
|
+
# contiguous or in order, but cell pointers are contiguous and in order.
|
150
|
+
#
|
151
|
+
# Cell content makes use of variable length integers. A variable
|
152
|
+
# length integer is 1 to 9 bytes where the lower 7 bits of each
|
153
|
+
# byte are used. The integer consists of all bytes that have bit 8 set and
|
154
|
+
# the first byte with bit 8 clear. The most significant byte of the integer
|
155
|
+
# appears first. A variable-length integer may not be more than 9 bytes long.
|
156
|
+
# As a special case, all 8 bytes of the 9th byte are used as data. This
|
157
|
+
# allows a 64-bit integer to be encoded in 9 bytes.
|
158
|
+
#
|
159
|
+
# 0x00 becomes 0x00000000
|
160
|
+
# 0x7f becomes 0x0000007f
|
161
|
+
# 0x81 0x00 becomes 0x00000080
|
162
|
+
# 0x82 0x00 becomes 0x00000100
|
163
|
+
# 0x80 0x7f becomes 0x0000007f
|
164
|
+
# 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
|
165
|
+
# 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
|
166
|
+
#
|
167
|
+
# Variable length integers are used for rowids and to hold the number of
|
168
|
+
# bytes of key and data in a btree cell.
|
169
|
+
#
|
170
|
+
# The content of a cell looks like this:
|
171
|
+
#
|
172
|
+
# SIZE DESCRIPTION
|
173
|
+
# 4 Page number of the left child. Omitted if leaf flag is set.
|
174
|
+
# var Number of bytes of data. Omitted if the zerodata flag is set.
|
175
|
+
# var Number of bytes of key. Or the key itself if intkey flag is set.
|
176
|
+
# * Payload
|
177
|
+
# 4 First page of the overflow chain. Omitted if no overflow
|
178
|
+
#
|
179
|
+
# Overflow pages form a linked list. Each page except the last is completely
|
180
|
+
# filled with data (pagesize - 4 bytes). The last page can have as little
|
181
|
+
# as 1 byte of data.
|
182
|
+
#
|
183
|
+
# SIZE DESCRIPTION
|
184
|
+
# 4 Page number of next overflow page
|
185
|
+
# * Data
|
186
|
+
#
|
187
|
+
# Freelist pages come in two subtypes: trunk pages and leaf pages. The
|
188
|
+
# file header points to the first in a linked list of trunk page. Each trunk
|
189
|
+
# page points to multiple leaf pages. The content of a leaf page is
|
190
|
+
# unspecified. A trunk page looks like this:
|
191
|
+
#
|
192
|
+
# SIZE DESCRIPTION
|
193
|
+
# 4 Page number of next trunk page
|
194
|
+
# 4 Number of leaf pointers on this page
|
195
|
+
# * zero or more pages numbers of leaves
|
196
|
+
|
197
|
+
require 'ostruct'
|
198
|
+
require 'enumerator'
|
199
|
+
|
200
|
+
require 'binary_struct'
|
201
|
+
|
202
|
+
require_relative 'MiqSqlite3Page'
|
203
|
+
require_relative 'MiqSqlite3Table'
|
204
|
+
|
205
|
+
module MiqSqlite3DB
|
206
|
+
# Database Header (all numbers in Big-Endian format)
|
207
|
+
DBHEADER = BinaryStruct.new([ # OFFSET SIZE DESCRIPTION
|
208
|
+
'a16', 'magic', # 0 16 Header string: "SQLite format 3\000"
|
209
|
+
'n', 'page_size', # 16 2 Page size in bytes.
|
210
|
+
'C', 'write_version', # 18 1 File format write version
|
211
|
+
'C', 'read_version', # 19 1 File format read version
|
212
|
+
'C', 'unused_space', # 20 1 Bytes of unused space at the end of each page
|
213
|
+
'C', 'max_payload_node_fraction', # 21 1 Max embedded payload fraction
|
214
|
+
'C', 'min_payload_node_fraction', # 22 1 Min embedded payload fraction
|
215
|
+
'C', 'min_payload_leaf_fraction', # 23 1 Min leaf payload fraction
|
216
|
+
'N', 'file_change_counter', # 24 4 File change counter
|
217
|
+
'a4', 'reserved', # 28 4 Reserved for future use
|
218
|
+
'N', 'first_freelist_page', # 32 4 First freelist page
|
219
|
+
'N', 'number_freelist_pages', # 36 4 Number of freelist pages in the file
|
220
|
+
'N15', 'meta_values', # 40 60 15 4-byte meta values passed to higher layers
|
221
|
+
])
|
222
|
+
|
223
|
+
SIZEOF_DBHEADER = DBHEADER.size
|
224
|
+
|
225
|
+
SQLITE_MAX_PAGE_SIZE = 32768
|
226
|
+
|
227
|
+
class MiqSqlite3
|
228
|
+
attr_reader :pageSize, :maxLocal, :minLocal, :usableSize, :maxLeaf, :minLeaf, :npages
|
229
|
+
|
230
|
+
def initialize(fileName = nil, fs = nil)
|
231
|
+
@fs = fs unless fs.nil?
|
232
|
+
open(fileName) unless fileName.nil?
|
233
|
+
|
234
|
+
if @pageSize < 512 || @pageSize > SQLITE_MAX_PAGE_SIZE || ((@pageSize - 1) & @pageSize) != 0
|
235
|
+
# @pageSize = 0
|
236
|
+
# sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
237
|
+
@maxEmbedFrac = 64 # 25.0%
|
238
|
+
@minEmbedFrac = 32 # 12.5%
|
239
|
+
@minLeafFrac = 32 # 12.5%
|
240
|
+
@nReserve = 0
|
241
|
+
else
|
242
|
+
@nReserve = @header.unused_space
|
243
|
+
@maxEmbedFrac = @header.max_payload_node_fraction
|
244
|
+
@minEmbedFrac = @header.min_payload_node_fraction
|
245
|
+
@minLeafFrac = @header.min_payload_leaf_fraction
|
246
|
+
@pageSizeFixed = true
|
247
|
+
end
|
248
|
+
|
249
|
+
@usableSize = @pageSize - @nReserve
|
250
|
+
# assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
|
251
|
+
|
252
|
+
#########################################################################
|
253
|
+
# /* maxLocal is the maximum amount of payload to store locally for
|
254
|
+
# ** a cell. Make sure it is small enough so that at least minFanout
|
255
|
+
# ** cells can will fit on one page. We assume a 10-byte page header.
|
256
|
+
# ** Besides the payload, the cell must store:
|
257
|
+
# ** 2-byte pointer to the cell
|
258
|
+
# ** 4-byte child pointer
|
259
|
+
# ** 9-byte nKey value
|
260
|
+
# ** 4-byte nData value
|
261
|
+
# ** 4-byte overflow page pointer
|
262
|
+
# ** So a cell consists of a 2-byte poiner, a header which is as much as
|
263
|
+
# ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
|
264
|
+
# ** page pointer.
|
265
|
+
#########################################################################
|
266
|
+
@maxLocal = (@usableSize - 12) * @maxEmbedFrac / 255 - 23
|
267
|
+
@minLocal = (@usableSize - 12) * @minEmbedFrac / 255 - 23
|
268
|
+
@maxLeaf = @usableSize - 35
|
269
|
+
@minLeaf = (@usableSize - 12) * @minLeafFrac / 255 - 23
|
270
|
+
end
|
271
|
+
|
272
|
+
# Open the database
|
273
|
+
def open(filename)
|
274
|
+
# Get header & check.
|
275
|
+
@filename = filename
|
276
|
+
@file = fileOpen(@filename)
|
277
|
+
@header = OpenStruct.new(DBHEADER.decode(@file.read(SIZEOF_DBHEADER)))
|
278
|
+
@pageSize = @header.page_size
|
279
|
+
@npages = fileSize / @pageSize
|
280
|
+
|
281
|
+
raise "#{@filename} is not a SQLite3 Database." if @header.magic != "SQLite format 3\000"
|
282
|
+
raise "#{@filename} is corrupt -- has incomplete pages (pagesize=#{@pageSize})" if (@pageSize * @npages) != fileSize
|
283
|
+
end
|
284
|
+
|
285
|
+
# Close the database
|
286
|
+
def close
|
287
|
+
@db.close if @db
|
288
|
+
@file.close if @file
|
289
|
+
@db = @header = @file = @filename = nil
|
290
|
+
end
|
291
|
+
|
292
|
+
def each_page
|
293
|
+
for pagenum in 1..npages
|
294
|
+
yield MiqSqlite3Page.getPage(self, pagenum)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def readPage(pagenum)
|
299
|
+
where = (pagenum - 1) * @pageSize
|
300
|
+
@file.seek(where)
|
301
|
+
@file.read(@pageSize)
|
302
|
+
end
|
303
|
+
|
304
|
+
def size
|
305
|
+
fileSize
|
306
|
+
end
|
307
|
+
|
308
|
+
def table_names
|
309
|
+
MiqSqlite3Table.table_names(self)
|
310
|
+
end
|
311
|
+
|
312
|
+
def getTable(name)
|
313
|
+
MiqSqlite3Table.getTable(self, name)
|
314
|
+
end
|
315
|
+
|
316
|
+
private
|
317
|
+
|
318
|
+
def fileSize
|
319
|
+
# Use MiqFS or File depending on @fs.
|
320
|
+
return File.size(@filename) if @fs.nil?
|
321
|
+
@fs.fileSize(@filename)
|
322
|
+
end
|
323
|
+
|
324
|
+
def fileOpen(_fileName)
|
325
|
+
# Return a file object using MiqFS or File as the case may be.
|
326
|
+
return File.open(@filename, "rb") if @fs.nil?
|
327
|
+
@fs.fileOpen(@filename, "r")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
require 'enumerator'
|
5
|
+
|
6
|
+
require 'binary_struct'
|
7
|
+
require_relative 'MiqSqlite3Util'
|
8
|
+
require_relative 'MiqSqlite3Page'
|
9
|
+
|
10
|
+
module MiqSqlite3DB
|
11
|
+
class MiqSqlite3Cell
|
12
|
+
attr_accessor :left_child, :key, :data, :pointer
|
13
|
+
|
14
|
+
def initialize(page, pointer)
|
15
|
+
@page = page
|
16
|
+
@pointer = pointer
|
17
|
+
@data = @key = @left_child = @fields = nil
|
18
|
+
|
19
|
+
offset = nData = 0
|
20
|
+
unless @page.leaf
|
21
|
+
@left_child = @page.buf[@pointer + offset, 4].unpack('N')[0]
|
22
|
+
offset += 4
|
23
|
+
end
|
24
|
+
|
25
|
+
if @page.hasData
|
26
|
+
nData, count = MiqSqlite3DB.variableInteger(@page.buf[@pointer + offset, 9])
|
27
|
+
offset += count
|
28
|
+
end
|
29
|
+
|
30
|
+
nKey, count = MiqSqlite3DB.variableInteger(@page.buf[@pointer + offset, 9])
|
31
|
+
offset += count
|
32
|
+
if @page.intKey
|
33
|
+
@key = nKey
|
34
|
+
nKey = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
getPayload(nData, nKey, offset)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fields
|
41
|
+
return @fields if @fields
|
42
|
+
|
43
|
+
return nil if @data.nil?
|
44
|
+
len = @data[0].ord
|
45
|
+
@fields = []
|
46
|
+
offset = len
|
47
|
+
byte = 1
|
48
|
+
while byte < len
|
49
|
+
val, count = MiqSqlite3DB.variableInteger(@data[byte..-1])
|
50
|
+
byte += count
|
51
|
+
flen, type = decodeField(val)
|
52
|
+
|
53
|
+
field = {}
|
54
|
+
if flen == 0
|
55
|
+
if type == 'null'
|
56
|
+
field['type'] = 'text'
|
57
|
+
field['data'] = nil
|
58
|
+
else
|
59
|
+
field['type'] = 'boolean'
|
60
|
+
field['data'] = true if type == 'true'
|
61
|
+
field['data'] = false if type == 'false'
|
62
|
+
end
|
63
|
+
else
|
64
|
+
field['len'] = flen
|
65
|
+
field['type'] = type
|
66
|
+
fdata = @data[offset, flen]
|
67
|
+
offset += flen
|
68
|
+
if type == 'integer'
|
69
|
+
fdata = "\x00".concat(fdata) if flen == 3
|
70
|
+
field['data'] = fdata.unpack("C")[0] if flen == 1
|
71
|
+
field['data'] = fdata.unpack("n")[0] if flen == 2
|
72
|
+
field['data'] = fdata.unpack("N")[0] if flen == 3 || flen == 4
|
73
|
+
else
|
74
|
+
field['data'] = fdata
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@fields << field
|
78
|
+
end
|
79
|
+
@fields
|
80
|
+
end
|
81
|
+
|
82
|
+
def dump
|
83
|
+
puts "======== Dumping Cell ========="
|
84
|
+
puts "Page: #{@page.pagenum}"
|
85
|
+
puts "Cell: #{@pointer}"
|
86
|
+
puts "Left Child: #{@left_child}" if @left_child
|
87
|
+
puts "Key: #{@key}" if @key
|
88
|
+
puts "Data: #{@data}" if @data
|
89
|
+
puts "Data Length: #{@data.size}" if @data
|
90
|
+
fields.each do |f|
|
91
|
+
p f
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def getPayload(nData, nKey, offset)
|
98
|
+
nPayload = nData + nKey
|
99
|
+
if nPayload <= @page.maxLocal
|
100
|
+
## This is the (easy) common case where the entire payload fits
|
101
|
+
## on the local page. No overflow is required.
|
102
|
+
nLocal = nPayload
|
103
|
+
iOverflow = 0
|
104
|
+
nSize = nPayload + offset # Total size of cell content in bytes
|
105
|
+
nSize = 4 if nSize < 4 # Minimum cell size is 4
|
106
|
+
@data = @page.buf[@pointer + offset, nData] if nData > 0
|
107
|
+
@key = @page.buf[@pointer + offset + nData, nKey] if nKey > 0
|
108
|
+
else
|
109
|
+
## If the payload will not fit completely on the local page, we have
|
110
|
+
## to decide how much to store locally and how much to spill onto
|
111
|
+
## overflow pages. The strategy is to minimize the amount of unused
|
112
|
+
## space on overflow pages while keeping the amount of local storage
|
113
|
+
## in between minLocal and maxLocal.
|
114
|
+
##
|
115
|
+
## Warning: changing the way overflow payload is distributed in any
|
116
|
+
## way will result in an incompatible file format.
|
117
|
+
##
|
118
|
+
minLocal = @page.minLocal # Minimum amount of payload held locally
|
119
|
+
maxLocal = @page.maxLocal # Maximum amount of payload held locally
|
120
|
+
surplus = minLocal + (nPayload - minLocal) % (@db.usableSize - 4) # Overflow payload available for local storage
|
121
|
+
nLocal = (surplus <= maxLocal) ? surplus : minLocal
|
122
|
+
iOverflow = nLocal + offset
|
123
|
+
nSize = iOverflow + 4
|
124
|
+
@overflow = @page.buf[@pointer + iOverflow, 4].unpack('N')[0]
|
125
|
+
raise "cell (#{@number}): PAYLOAD OVERFLOW NOT SUPPORTED"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# ** The following table describes the various storage classes for data:
|
130
|
+
# **
|
131
|
+
# ** serial type bytes of data type
|
132
|
+
# ** -------------- --------------- ---------------
|
133
|
+
# ** 0 0 NULL
|
134
|
+
# ** 1 1 signed integer
|
135
|
+
# ** 2 2 signed integer
|
136
|
+
# ** 3 3 signed integer
|
137
|
+
# ** 4 4 signed integer
|
138
|
+
# ** 5 6 signed integer
|
139
|
+
# ** 6 8 signed integer
|
140
|
+
# ** 7 8 IEEE float
|
141
|
+
# ** 8 0 Integer constant 0
|
142
|
+
# ** 9 0 Integer constant 1
|
143
|
+
# ** 10,11 reserved for expansion
|
144
|
+
# ** N>=12 and even (N-12)/2 BLOB
|
145
|
+
# ** N>=13 and odd (N-13)/2 text
|
146
|
+
def decodeField(serial_type)
|
147
|
+
case serial_type
|
148
|
+
when 0 then return 0, 'null'
|
149
|
+
when 1 then return 1, 'integer'
|
150
|
+
when 2 then return 2, 'integer'
|
151
|
+
when 3 then return 3, 'integer'
|
152
|
+
when 4 then return 4, 'integer'
|
153
|
+
when 6 then return 6, 'integer'
|
154
|
+
when 8 then return 8, 'integer'
|
155
|
+
when 7 then return 8, 'float'
|
156
|
+
when 8 then return 0, 'false'
|
157
|
+
when 9 then return 0, 'true'
|
158
|
+
when 10 then raise "Unknown Column Type #{serial_type}"
|
159
|
+
when 11 then raise "Unknown Column Type #{serial_type}"
|
160
|
+
end
|
161
|
+
|
162
|
+
type = serial_type.even? ? 'blob' : 'text'
|
163
|
+
len = (serial_type - 12) / 2
|
164
|
+
return len, type
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'enumerator'
|
3
|
+
|
4
|
+
require 'binary_struct'
|
5
|
+
require_relative 'MiqSqlite3Util'
|
6
|
+
require_relative 'MiqSqlite3Cell'
|
7
|
+
|
8
|
+
module MiqSqlite3DB
|
9
|
+
class MiqSqlite3Page
|
10
|
+
# Page Header.
|
11
|
+
HEADER = BinaryStruct.new([ # All integers are in Big-Endian (or Network) Order
|
12
|
+
# OFFSET SIZE DESCRIPTION
|
13
|
+
'C', 'flags', # 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
|
14
|
+
'n', 'offset2freeblock', # 1 2 byte offset to the first freeblock
|
15
|
+
'n', 'num_cells', # 3 2 number of cells on this page
|
16
|
+
'n', 'offset2cell', # 5 2 first byte of the cell content area
|
17
|
+
'C', 'num_fragmented_free_bytes', # 7 1 number of fragmented free bytes
|
18
|
+
'N', 'right_child', # 8 4 Right child (the Ptr(N) value). Omitted on leaves.
|
19
|
+
])
|
20
|
+
|
21
|
+
SIZEOF_HEADER = HEADER.size
|
22
|
+
|
23
|
+
#####################################
|
24
|
+
## Class Methods
|
25
|
+
#####################################
|
26
|
+
|
27
|
+
def self.getPage(db, pagenum)
|
28
|
+
MiqSqlite3Page.new(db.readPage(pagenum), db, pagenum)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.intKey?(f)
|
32
|
+
f & 0x01 == 0x01
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.zeroData?(f)
|
36
|
+
f & 0x02 == 0x02
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.leafData?(f)
|
40
|
+
f & 0x04 == 0x04
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.leaf?(f)
|
44
|
+
f & 0x08 == 0x08
|
45
|
+
end
|
46
|
+
|
47
|
+
#####################################
|
48
|
+
## Instance Methods
|
49
|
+
#####################################
|
50
|
+
|
51
|
+
attr_accessor :buf, :leaf, :hasData, :intKey, :maxLocal, :minLocal, :pagenum
|
52
|
+
|
53
|
+
def initialize(buf, db, pagenum)
|
54
|
+
raise "Nil buffer" if buf.nil?
|
55
|
+
@pagenum = pagenum
|
56
|
+
@db = db
|
57
|
+
@buf = buf
|
58
|
+
@pagesize = buf.size
|
59
|
+
skip = (pagenum == 1) ? SIZEOF_DBHEADER : 0
|
60
|
+
@header = OpenStruct.new(HEADER.decode(@buf[skip, SIZEOF_HEADER]))
|
61
|
+
decodeFlags
|
62
|
+
headerSize = SIZEOF_HEADER - @childPtrSize
|
63
|
+
@data = buf[skip + headerSize..-1]
|
64
|
+
@cells = @cellPointers = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def decodeFlags
|
68
|
+
@leafData = MiqSqlite3Page.leafData?(@header.flags)
|
69
|
+
@zeroData = MiqSqlite3Page.zeroData?(@header.flags)
|
70
|
+
@leaf = MiqSqlite3Page.leaf?(@header.flags)
|
71
|
+
@childPtrSize = @leaf ? 4 : 0
|
72
|
+
if MiqSqlite3Page.leafData?(@header.flags)
|
73
|
+
@intKey = MiqSqlite3Page.intKey?(@header.flags)
|
74
|
+
@maxLocal = @db.maxLeaf
|
75
|
+
@minLocal = @db.minLeaf
|
76
|
+
else
|
77
|
+
@intKey = false
|
78
|
+
@maxLocal = @db.maxLocal
|
79
|
+
@minLocal = @db.minLocal
|
80
|
+
end
|
81
|
+
@hasData = !(@zeroData || (!@leaf && @leafData))
|
82
|
+
end
|
83
|
+
|
84
|
+
def each_child
|
85
|
+
each_cell do |cell|
|
86
|
+
yield cell.left_child if cell.left_child
|
87
|
+
end
|
88
|
+
yield @header.right_child unless @leaf
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_cell
|
92
|
+
initCells if @cells.nil?
|
93
|
+
@cells.each { |c| yield c }
|
94
|
+
end
|
95
|
+
|
96
|
+
def leaves
|
97
|
+
each_child do |child|
|
98
|
+
MiqSqlite3Page.getPage(@db, child).leaves { |p| yield p }
|
99
|
+
end
|
100
|
+
yield self if @leaf
|
101
|
+
end
|
102
|
+
|
103
|
+
def dump
|
104
|
+
puts "================="
|
105
|
+
puts "Page: #{@pagenum}"
|
106
|
+
# puts "Page Size: #{@pagesize}"
|
107
|
+
# puts "Data Size: #{@data.size}"
|
108
|
+
# puts "Flags (Raw): #{@header.flags}"
|
109
|
+
puts "Flags: #{flags2str}"
|
110
|
+
puts "Offset to First Freeblock: #{@header.offset2freeblock}"
|
111
|
+
puts "Offset to Cell Content Area: #{@header.offset2cell}"
|
112
|
+
puts "Number of Cells: #{@header.num_cells}"
|
113
|
+
puts "Number of Fragmented Free Bytes: #{@header.num_fragmented_free_bytes}"
|
114
|
+
puts "Right Child: #{@header.right_child}" unless @leaf
|
115
|
+
puts "Cell Pointers: #{cellPointers2String}"
|
116
|
+
|
117
|
+
# each_cell { |cell| p cell }
|
118
|
+
# each_child { |kid| puts "Child: #{kid}"}
|
119
|
+
end
|
120
|
+
|
121
|
+
def flags2str
|
122
|
+
str = ""
|
123
|
+
str << "IntKey " if @intKey
|
124
|
+
str << "ZeroData " if @zeroData
|
125
|
+
str << "LeafData " if @leafData
|
126
|
+
str << "Leaf " if @leaf
|
127
|
+
str.chomp
|
128
|
+
end
|
129
|
+
|
130
|
+
def cellPointers2String
|
131
|
+
initCellPointers if @cellPointers.nil?
|
132
|
+
str = ""
|
133
|
+
@cellPointers.each { |p| str << "#{p} " }
|
134
|
+
return nil if str == ""
|
135
|
+
str.chomp
|
136
|
+
end
|
137
|
+
|
138
|
+
def initCellPointers
|
139
|
+
@cellPointers = []
|
140
|
+
for i in 1..@header.num_cells
|
141
|
+
@cellPointers << @data[(i - 1) * 2, 2].unpack('n')[0]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def initCells
|
146
|
+
@cells = []
|
147
|
+
initCellPointers if @cellPointers.nil?
|
148
|
+
@cellPointers.each { |p| @cells << MiqSqlite3Cell.new(self, p) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|