Files
ChuSQL/chusql-storage/src/heap.rs
T

486 lines
15 KiB
Rust
Raw Normal View History

2026-09-25 14:21:55 +08:00
use std::collections::HashSet;
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-25 14:21:55 +08:00
// 堆表:行按插入顺序进页,支持全表扫描、按索引取行、行级删除。
const HEADER_SIZE: usize = 2;
const SLOT_SIZE: usize = 8;
const SLOT_DIR_START: usize = HEADER_SIZE;
2026-09-24 21:28:49 +08:00
/// 读一个小端 u16
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
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
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
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
fn pack_position(page_id: PageId, slot: u16) -> u64 {
(page_id << 16) | (slot as u64)
}
2026-09-24 21:28:49 +08:00
/// 从 u64 解回 (页号, 槽位号)
fn unpack_position(v: u64) -> (PageId, u16) {
(v >> 16, (v & 0xFFFF) as u16)
}
2026-09-25 14:21:55 +08:00
/// 取行里某一列的整数值(不是整数就没有)
fn row_int(row: &Row, column: &str) -> Option<i64> {
match row.get(column) {
Some(serde_json::Value::Number(n)) => n.as_i64(),
_ => None,
}
}
pub struct HeapPage;
impl HeapPage {
2026-09-24 21:28:49 +08:00
/// 这一页有几个槽位
pub fn slot_count(page: &Page) -> u16 {
read_u16(&page.data, 0)
}
2026-09-24 21:28:49 +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
/// 空闲空间下界
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
/// 空闲空间上界
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
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-25 17:41:51 +08:00
/// 删掉一个槽位,只把长度置 0
2026-09-25 14:21:55 +08:00
pub fn delete_tuple(page: &mut Page, slot: u16) -> bool {
let n = Self::slot_count(page);
if slot >= n {
return false;
}
let slot_off = SLOT_DIR_START + (slot as usize) * SLOT_SIZE;
if read_u32(&page.data, slot_off + 4) == 0 {
return false;
}
write_u32(&mut page.data, slot_off + 4, 0);
true
}
2026-09-24 21:28:49 +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
/// 所有槽位号
pub fn iter_slots(page: &Page) -> Vec<u16> {
(0..Self::slot_count(page)).collect()
}
}
2026-09-25 14:21:55 +08:00
struct NamedIndex {
column: String,
tree: DiskBTree,
}
pub struct HeapTable {
file: PageFile,
2026-09-25 14:21:55 +08:00
indexes: Vec<NamedIndex>,
}
impl HeapTable {
2026-09-24 21:28:49 +08:00
/// 打开表(不带索引)
2026-09-25 14:21:55 +08:00
pub fn open<P: AsRef<Path>>(path: P, page_size: usize, pool_size: usize) -> io::Result<Self> {
Ok(HeapTable {
2026-09-24 21:28:49 +08:00
file: PageFile::with_options(path, page_size, pool_size)?,
2026-09-25 14:21:55 +08:00
indexes: Vec::new(),
})
}
2026-09-25 17:41:51 +08:00
/// 打开表并挂上 id 列索引
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,
) -> io::Result<Self> {
2026-09-25 14:21:55 +08:00
let mut t = HeapTable {
2026-09-24 21:28:49 +08:00
file: PageFile::with_options(path, page_size, pool_size)?,
2026-09-25 14:21:55 +08:00
indexes: Vec::new(),
};
t.attach_index("id", index_path, page_size, btree_order, pool_size)?;
Ok(t)
}
2026-09-25 17:41:51 +08:00
/// 打开表并挂上多组索引
2026-09-25 14:21:55 +08:00
pub fn open_with_indexes<P: AsRef<Path>>(
path: P,
indexes: &[(String, std::path::PathBuf)],
page_size: usize,
btree_order: usize,
pool_size: usize,
) -> io::Result<Self> {
let mut t = HeapTable {
file: PageFile::with_options(path, page_size, pool_size)?,
indexes: Vec::new(),
};
for (col, p) in indexes {
t.attach_index(col, p, page_size, btree_order, pool_size)?;
}
Ok(t)
}
2026-09-25 17:41:51 +08:00
/// 挂上一棵索引树,不填数据
2026-09-25 14:21:55 +08:00
fn attach_index<P: AsRef<Path>>(
&mut self,
column: &str,
path: P,
page_size: usize,
btree_order: usize,
pool_size: usize,
) -> io::Result<()> {
let tree = DiskBTree::open(path, page_size, btree_order, pool_size)?;
self.indexes.push(NamedIndex {
column: column.to_string(),
tree,
});
Ok(())
}
/// 这一列有索引吗
pub fn has_index(&self, column: &str) -> bool {
self.indexes.iter().any(|i| i.column == column)
}
2026-09-25 17:41:51 +08:00
/// 建索引:要求这一列取值唯一
2026-09-25 14:21:55 +08:00
pub fn build_index(
&mut self,
column: &str,
path: &Path,
page_size: usize,
btree_order: usize,
pool_size: usize,
) -> io::Result<()> {
if self.has_index(column) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!("index on column \"{}\" already exists", column),
));
}
let mut seen: HashSet<i64> = HashSet::new();
let mut entries: Vec<(i64, u64)> = Vec::new();
for (page_id, slot, r) in self.scan_with_positions()? {
let Some(k) = row_int(&r, column) else {
2026-09-25 17:41:51 +08:00
continue;
2026-09-25 14:21:55 +08:00
};
if !seen.insert(k) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
2026-09-25 17:41:51 +08:00
"column \"{}\" has duplicate value {} -- non-unique indexes are not supported yet",
2026-09-25 14:21:55 +08:00
column, k
),
));
}
entries.push((k, pack_position(page_id, slot)));
}
self.attach_index(column, path, page_size, btree_order, pool_size)?;
let i = self.indexes.len() - 1;
2026-09-25 17:41:51 +08:00
self.indexes[i].tree.clear()?;
2026-09-25 14:21:55 +08:00
for (k, pos) in entries {
self.indexes[i].tree.insert(k, pos)?;
}
Ok(())
}
/// 摘掉一个索引(不删文件)
pub fn detach_index(&mut self, column: &str) -> bool {
match self.indexes.iter().position(|i| i.column == column) {
None => false,
Some(i) => {
self.indexes.remove(i);
true
}
}
}
2026-09-25 17:41:51 +08:00
/// 插入一行,并顺手写各索引
2026-09-25 14:21:55 +08:00
pub fn insert_row(&mut self, row: &Row) -> io::Result<()> {
self.insert_row_returning_position(row).map(|_| ())
}
/// 插入一行并返回它的位置
pub fn insert_row_returning_position(&mut self, row: &Row) -> io::Result<(PageId, u16)> {
let mut pending: Vec<(usize, i64)> = Vec::new();
for (i, idx) in self.indexes.iter_mut().enumerate() {
let Some(k) = row_int(row, &idx.column) else {
2026-09-25 17:41:51 +08:00
continue;
2026-09-25 14:21:55 +08:00
};
if idx.tree.get(k)?.is_some() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("duplicate key {} on indexed column \"{}\"", k, idx.column),
));
}
pending.push((i, k));
}
let (page_id, slot) = self.insert_returning_position(row)?;
let pos = pack_position(page_id, slot);
for (i, k) in pending {
self.indexes[i].tree.insert(k, pos)?;
}
Ok((page_id, slot))
}
2026-09-25 17:41:51 +08:00
/// 批量插入,落盘由调用方负责
2026-09-25 14:21:55 +08:00
pub fn insert_rows(&mut self, rows: &[Row]) -> io::Result<()> {
for r in rows {
self.insert_row(r)?;
}
Ok(())
}
/// 插入一行,只进堆表、不碰索引
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));
}
}
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-25 14:21:55 +08:00
/// 按 `id` 取一行
pub fn get_by_key(&mut self, key: i64) -> io::Result<Option<Row>> {
self.get_by_column_key("id", key)
}
2026-09-25 14:21:55 +08:00
/// 按某一列的索引取一行
pub fn get_by_column_key(&mut self, column: &str, key: i64) -> io::Result<Option<Row>> {
let Some(idx) = self.indexes.iter_mut().find(|i| i.column == column) else {
return Ok(None);
};
let Some(pos) = idx.tree.get(key)? else {
return Ok(None);
};
let (page_id, slot) = unpack_position(pos);
self.read_at(page_id, slot)
}
2026-09-24 21:28:49 +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 {
None => Ok(None),
Some(bytes) => {
let row: Row = serde_json::from_slice(&bytes).map_err(io::Error::other)?;
Ok(Some(row))
}
}
}
2026-09-25 17:41:51 +08:00
/// 按位置删一行并返回它
2026-09-25 14:21:55 +08:00
pub fn delete_at(&mut self, page_id: PageId, slot: u16) -> io::Result<Option<Row>> {
let row = self.read_at(page_id, slot)?;
let existed = self.file.update_page(page_id, |p| HeapPage::delete_tuple(p, slot))?;
if !existed {
return Ok(None);
}
if let Some(r) = &row {
for idx in &mut self.indexes {
if let Some(k) = row_int(r, &idx.column) {
idx.tree.delete(k)?;
}
}
}
2026-09-25 14:21:55 +08:00
Ok(row)
}
2026-09-25 17:41:51 +08:00
/// 按 id 批量删行,返回删掉的行
2026-09-25 14:21:55 +08:00
pub fn delete_by_keys(&mut self, keys: &[i64]) -> io::Result<Vec<Row>> {
let mut positions: Vec<(PageId, u16)> = Vec::new();
if self.has_index("id") {
for &k in keys {
let pos = self
.indexes
.iter_mut()
.find(|i| i.column == "id")
.and_then(|i| i.tree.get(k).ok().flatten());
if let Some(p) = pos {
positions.push(unpack_position(p));
}
}
} else {
let mut wanted: Vec<i64> = keys.to_vec();
wanted.sort_unstable();
for (page_id, slot, r) in self.scan_with_positions()? {
if let Some(k) = row_int(&r, "id") {
if wanted.binary_search(&k).is_ok() {
positions.push((page_id, slot));
}
}
}
}
let mut deleted: Vec<Row> = Vec::new();
for (page_id, slot) in positions {
if let Some(r) = self.delete_at(page_id, slot)? {
deleted.push(r);
}
}
Ok(deleted)
}
/// 全表扫描
pub fn scan(&mut self) -> io::Result<Vec<Row>> {
Ok(self
.scan_with_positions()?
.into_iter()
.map(|(_, _, r)| r)
.collect())
}
/// 全表扫描,同时给出每行的位置(建索引用)
pub fn scan_with_positions(&mut self) -> io::Result<Vec<(PageId, u16, Row)>> {
let n = self.file.num_pages()?;
let mut out = Vec::new();
for i in 0..n {
let tuples = self.file.with_page(i, |p| {
let mut v = Vec::new();
for slot in HeapPage::iter_slots(p) {
if let Some(bytes) = HeapPage::get_tuple(p, slot) {
v.push((slot, bytes));
}
}
v
})?;
for (slot, bytes) in tuples {
let row: Row = serde_json::from_slice(&bytes).map_err(io::Error::other)?;
out.push((i, slot, row));
}
}
Ok(out)
}
/// 整表改写并重建所有索引
pub fn replace_all(&mut self, rows: &[Row]) -> io::Result<()> {
self.file.truncate()?;
2026-09-25 14:21:55 +08:00
for idx in &mut self.indexes {
idx.tree.clear()?;
}
for r in rows {
let (page_id, slot) = self.insert_returning_position(r)?;
2026-09-25 14:21:55 +08:00
let pos = pack_position(page_id, slot);
for idx in &mut self.indexes {
if let Some(k) = row_int(r, &idx.column) {
idx.tree.insert(k, pos)?;
}
}
}
Ok(())
}
2026-09-24 21:28:49 +08:00
2026-09-25 14:21:55 +08:00
/// 把脏页写回(索引也一起)
2026-09-24 21:28:49 +08:00
pub fn flush(&mut self) -> io::Result<()> {
2026-09-25 14:21:55 +08:00
for idx in &mut self.indexes {
idx.tree.flush()?;
2026-09-24 21:28:49 +08:00
}
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() }
}