2026-09-24 15:37:55 +08:00
|
|
|
use std::io;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
use crate::btree::DiskBTree;
|
|
|
|
|
use crate::page::PageId;
|
|
|
|
|
use crate::page::{Page, PageFile};
|
|
|
|
|
use crate::protocol::Row;
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
// 堆表:行按插入顺序进页,支持全表扫描与按索引取行。
|
2026-09-24 15:37:55 +08:00
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
// 页内布局
|
|
|
|
|
/// 页头:slot_count 占 2 字节
|
2026-09-24 15:37:55 +08:00
|
|
|
const HEADER_SIZE: usize = 2;
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 一个槽位占 8 字节
|
2026-09-24 15:37:55 +08:00
|
|
|
const SLOT_SIZE: usize = 8;
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 槽位目录起点
|
2026-09-24 15:37:55 +08:00
|
|
|
const SLOT_DIR_START: usize = HEADER_SIZE;
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 读一个小端 u16
|
2026-09-24 15:37:55 +08:00
|
|
|
fn read_u16(buf: &[u8], off: usize) -> u16 {
|
|
|
|
|
u16::from_le_bytes([buf[off], buf[off + 1]])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 写一个小端 u16
|
2026-09-24 15:37:55 +08:00
|
|
|
fn write_u16(buf: &mut [u8], off: usize, v: u16) {
|
|
|
|
|
buf[off..off + 2].copy_from_slice(&v.to_le_bytes());
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 读一个小端 u32
|
2026-09-24 15:37:55 +08:00
|
|
|
fn read_u32(buf: &[u8], off: usize) -> u32 {
|
|
|
|
|
u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 写一个小端 u32
|
2026-09-24 15:37:55 +08:00
|
|
|
fn write_u32(buf: &mut [u8], off: usize, v: u32) {
|
|
|
|
|
buf[off..off + 4].copy_from_slice(&v.to_le_bytes());
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
// 索引位置
|
|
|
|
|
/// (页号, 槽位号) 打包成 u64
|
2026-09-24 15:37:55 +08:00
|
|
|
fn pack_position(page_id: PageId, slot: u16) -> u64 {
|
|
|
|
|
(page_id << 16) | (slot as u64)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 从 u64 解回 (页号, 槽位号)
|
2026-09-24 15:37:55 +08:00
|
|
|
fn unpack_position(v: u64) -> (PageId, u16) {
|
|
|
|
|
(v >> 16, (v & 0xFFFF) as u16)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
// 页内操作
|
|
|
|
|
/// 只管一页里的字节
|
2026-09-24 15:37:55 +08:00
|
|
|
pub struct HeapPage;
|
|
|
|
|
|
|
|
|
|
impl HeapPage {
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 这一页有几个槽位
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn slot_count(page: &Page) -> u16 {
|
|
|
|
|
read_u16(&page.data, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 改写槽位数量
|
2026-09-24 15:37:55 +08:00
|
|
|
fn set_slot_count(page: &mut Page, v: u16) {
|
|
|
|
|
write_u16(&mut page.data, 0, v);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 空闲空间下界
|
2026-09-24 15:37:55 +08:00
|
|
|
fn free_start(page: &Page) -> usize {
|
|
|
|
|
SLOT_DIR_START + (Self::slot_count(page) as usize) * SLOT_SIZE
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 空闲空间上界
|
2026-09-24 15:37:55 +08:00
|
|
|
fn free_end(page: &Page) -> usize {
|
|
|
|
|
let n = Self::slot_count(page);
|
|
|
|
|
let mut min_off = page.data.len() as u32;
|
|
|
|
|
for i in 0..n as usize {
|
|
|
|
|
let slot_off = SLOT_DIR_START + i * SLOT_SIZE;
|
|
|
|
|
let off = read_u32(&page.data, slot_off);
|
|
|
|
|
let len = read_u32(&page.data, slot_off + 4);
|
|
|
|
|
if len > 0 && off < min_off {
|
|
|
|
|
min_off = off;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
min_off as usize
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 试插一行,放不下返回 None
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn insert_tuple(page: &mut Page, tuple: &[u8]) -> Option<u16> {
|
|
|
|
|
let n = Self::slot_count(page);
|
|
|
|
|
let free_start = Self::free_start(page);
|
|
|
|
|
let free_end = Self::free_end(page);
|
|
|
|
|
|
|
|
|
|
if free_start + SLOT_SIZE + tuple.len() > free_end {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let tuple_off = free_end - tuple.len();
|
|
|
|
|
page.data[tuple_off..tuple_off + tuple.len()].copy_from_slice(tuple);
|
|
|
|
|
|
|
|
|
|
let slot_off = SLOT_DIR_START + (n as usize) * SLOT_SIZE;
|
|
|
|
|
write_u32(&mut page.data, slot_off, tuple_off as u32);
|
|
|
|
|
write_u32(&mut page.data, slot_off + 4, tuple.len() as u32);
|
|
|
|
|
Self::set_slot_count(page, n + 1);
|
|
|
|
|
|
|
|
|
|
Some(n)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 读槽位里的行字节
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn get_tuple(page: &Page, slot: u16) -> Option<Vec<u8>> {
|
|
|
|
|
let n = Self::slot_count(page);
|
|
|
|
|
if slot >= n {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let slot_off = SLOT_DIR_START + (slot as usize) * SLOT_SIZE;
|
|
|
|
|
let off = read_u32(&page.data, slot_off) as usize;
|
|
|
|
|
let len = read_u32(&page.data, slot_off + 4) as usize;
|
|
|
|
|
if len == 0 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
Some(page.data[off..off + len].to_vec())
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 所有槽位号
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn iter_slots(page: &Page) -> Vec<u16> {
|
|
|
|
|
(0..Self::slot_count(page)).collect()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
// 堆表
|
|
|
|
|
/// 一个文件 = 若干页
|
2026-09-24 15:37:55 +08:00
|
|
|
pub struct HeapTable {
|
|
|
|
|
file: PageFile,
|
|
|
|
|
index: Option<DiskBTree>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HeapTable {
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 打开表(不带索引)
|
|
|
|
|
pub fn open<P: AsRef<Path>>(
|
|
|
|
|
path: P,
|
|
|
|
|
page_size: usize,
|
|
|
|
|
pool_size: usize,
|
|
|
|
|
) -> io::Result<Self> {
|
|
|
|
|
Ok(HeapTable {
|
|
|
|
|
file: PageFile::with_options(path, page_size, pool_size)?,
|
2026-09-24 15:37:55 +08:00
|
|
|
index: None,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 打开表 + 索引
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn open_indexed<P: AsRef<Path>, Q: AsRef<Path>>(
|
|
|
|
|
path: P,
|
|
|
|
|
index_path: Q,
|
|
|
|
|
page_size: usize,
|
|
|
|
|
btree_order: usize,
|
2026-09-24 21:28:49 +08:00
|
|
|
pool_size: usize,
|
2026-09-24 15:37:55 +08:00
|
|
|
) -> io::Result<Self> {
|
|
|
|
|
Ok(HeapTable {
|
2026-09-24 21:28:49 +08:00
|
|
|
file: PageFile::with_options(path, page_size, pool_size)?,
|
|
|
|
|
index: Some(DiskBTree::open(index_path, page_size, btree_order, pool_size)?),
|
2026-09-24 15:37:55 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 插入一行,不更新索引
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn insert(&mut self, row: &Row) -> io::Result<()> {
|
|
|
|
|
self.insert_returning_position(row).map(|_| ())
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 插入并返回行位置
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn insert_returning_position(&mut self, row: &Row) -> io::Result<(PageId, u16)> {
|
|
|
|
|
let tuple = serde_json::to_vec(row).map_err(io::Error::other)?;
|
|
|
|
|
|
|
|
|
|
if HEADER_SIZE + SLOT_SIZE + tuple.len() > self.file.page_size() {
|
|
|
|
|
return Err(io::Error::other(format!(
|
|
|
|
|
"tuple too large: {} bytes",
|
|
|
|
|
tuple.len()
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let n = self.file.num_pages()?;
|
|
|
|
|
if n > 0 {
|
|
|
|
|
let last_id = n - 1;
|
2026-09-24 23:56:07 +08:00
|
|
|
let mut slot = None;
|
|
|
|
|
self.file.update_page(last_id, |p| {
|
|
|
|
|
slot = HeapPage::insert_tuple(p, &tuple);
|
|
|
|
|
})?;
|
|
|
|
|
if let Some(s) = slot {
|
|
|
|
|
return Ok((last_id, s));
|
2026-09-24 15:37:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut p = self.file.append_page()?;
|
|
|
|
|
let page_id = p.id;
|
|
|
|
|
let slot = HeapPage::insert_tuple(&mut p, &tuple).expect("pre-checked fits");
|
|
|
|
|
self.file.write_page(&p)?;
|
|
|
|
|
Ok((page_id, slot))
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 插入一行并写索引
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn insert_keyed(&mut self, row: &Row, key: i64) -> io::Result<()> {
|
|
|
|
|
let (page_id, slot) = self.insert_returning_position(row)?;
|
|
|
|
|
if let Some(idx) = &mut self.index {
|
|
|
|
|
idx.insert(key, pack_position(page_id, slot))?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 按索引键取一行
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn get_by_key(&mut self, key: i64) -> io::Result<Option<Row>> {
|
|
|
|
|
let pos = match &mut self.index {
|
|
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(idx) => match idx.get(key)? {
|
|
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(p) => p,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
let (page_id, slot) = unpack_position(pos);
|
|
|
|
|
self.read_at(page_id, slot)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 按位置读一行
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn read_at(&mut self, page_id: PageId, slot: u16) -> io::Result<Option<Row>> {
|
2026-09-24 23:56:07 +08:00
|
|
|
let bytes = self.file.with_page(page_id, |p| HeapPage::get_tuple(p, slot))?;
|
|
|
|
|
match bytes {
|
2026-09-24 15:37:55 +08:00
|
|
|
None => Ok(None),
|
|
|
|
|
Some(bytes) => {
|
|
|
|
|
let row: Row = serde_json::from_slice(&bytes).map_err(io::Error::other)?;
|
|
|
|
|
Ok(Some(row))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 全表扫描
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn scan(&mut self) -> io::Result<Vec<Row>> {
|
|
|
|
|
let n = self.file.num_pages()?;
|
|
|
|
|
let mut rows = Vec::new();
|
|
|
|
|
for i in 0..n {
|
2026-09-24 23:56:07 +08:00
|
|
|
let tuples = self.file.with_page(i, |p| {
|
|
|
|
|
let mut out = Vec::new();
|
|
|
|
|
for slot in HeapPage::iter_slots(p) {
|
|
|
|
|
if let Some(bytes) = HeapPage::get_tuple(p, slot) {
|
|
|
|
|
out.push(bytes);
|
|
|
|
|
}
|
2026-09-24 15:37:55 +08:00
|
|
|
}
|
2026-09-24 23:56:07 +08:00
|
|
|
out
|
|
|
|
|
})?;
|
|
|
|
|
for bytes in tuples {
|
|
|
|
|
let row: Row = serde_json::from_slice(&bytes).map_err(io::Error::other)?;
|
|
|
|
|
rows.push(row);
|
2026-09-24 15:37:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(rows)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 整表改写并重建索引
|
2026-09-24 15:37:55 +08:00
|
|
|
pub fn replace_all(&mut self, rows: &[Row]) -> io::Result<()> {
|
|
|
|
|
self.file.truncate()?;
|
|
|
|
|
if let Some(idx) = &mut self.index {
|
|
|
|
|
idx.clear()?;
|
|
|
|
|
}
|
|
|
|
|
for r in rows {
|
|
|
|
|
let (page_id, slot) = self.insert_returning_position(r)?;
|
|
|
|
|
if let (Some(idx), Some(key)) = (&mut self.index, row_key(r)) {
|
|
|
|
|
idx.insert(key, pack_position(page_id, slot))?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-09-24 21:28:49 +08:00
|
|
|
|
|
|
|
|
/// 把脏页写回
|
|
|
|
|
pub fn flush(&mut self) -> io::Result<()> {
|
|
|
|
|
if let Some(idx) = &mut self.index {
|
|
|
|
|
idx.flush()?;
|
|
|
|
|
}
|
|
|
|
|
self.file.flush()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 缓冲池统计
|
|
|
|
|
/// 当前缓存页数
|
|
|
|
|
pub fn cached_pages(&self) -> usize { self.file.cached_pages() }
|
|
|
|
|
/// 命中次数
|
|
|
|
|
pub fn hits(&self) -> u64 { self.file.hits() }
|
|
|
|
|
/// 未命中次数
|
|
|
|
|
pub fn misses(&self) -> u64 { self.file.misses() }
|
|
|
|
|
/// 命中率
|
|
|
|
|
pub fn hit_rate(&self) -> f64 { self.file.hit_rate() }
|
2026-09-24 15:37:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
/// 取行里的 id 作索引键
|
2026-09-24 15:37:55 +08:00
|
|
|
fn row_key(row: &Row) -> Option<i64> {
|
|
|
|
|
match row.get("id") {
|
|
|
|
|
Some(serde_json::Value::Number(n)) => n.as_i64(),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|