use crate::{Result, StorageError};
use super::btree::{BTree, BTreeConfig};
use std::sync::{Arc, RwLock};
use std::path::PathBuf;
pub struct PrimaryKeyIndex {
btree: BTree,
auto_increment: Arc<RwLock<u64>>,
name: String,
}
impl PrimaryKeyIndex {
pub fn new(name: impl Into<String>, storage_path: PathBuf) -> Result<Self> {
let config = BTreeConfig {
unique_keys: true, allow_updates: true, ..Default::default()
};
Ok(Self {
btree: BTree::with_config(storage_path, config)?,
auto_increment: Arc::new(RwLock::new(1)),
name: name.into(),
})
}
pub fn insert(&mut self, pk: u64, row_id: u64) -> Result<()> {
self.btree.insert(pk, row_id)?;
let mut counter = self.auto_increment.write()
.map_err(|_| StorageError::Index("Lock poisoned".into()))?;
if pk >= *counter {
*counter = pk + 1;
}
Ok(())
}
pub fn insert_auto(&mut self, row_id: u64) -> Result<u64> {
let mut counter = self.auto_increment.write()
.map_err(|_| StorageError::Index("Lock poisoned".into()))?;
let pk = *counter;
*counter += 1;
drop(counter);
self.btree.insert(pk, row_id)?;
Ok(pk)
}
pub fn get(&self, pk: u64) -> Result<Option<u64>> {
self.btree.get(&pk)
}
pub fn update(&mut self, pk: u64, new_row_id: u64) -> Result<()> {
if !self.btree.contains_key(&pk)? {
return Err(StorageError::InvalidData(
format!("Primary key {} not found", pk)
));
}
self.btree.insert(pk, new_row_id)?;
Ok(())
}
pub fn delete(&mut self, pk: u64) -> Result<bool> {
Ok(self.btree.remove(&pk)?.is_some())
}
pub fn exists(&self, pk: u64) -> Result<bool> {
self.btree.contains_key(&pk)
}
pub fn range(&self, start: u64, end: u64) -> Result<Vec<(u64, u64)>> {
self.btree.range(&start, &end)
}
pub fn scan(&self) -> Result<Vec<(u64, u64)>> {
self.btree.scan()
}
pub fn min_pk(&self) -> Result<Option<u64>> {
self.btree.min_key()
}
pub fn max_pk(&self) -> Result<Option<u64>> {
self.btree.max_key()
}
pub fn next_auto_increment(&self) -> u64 {
*self.auto_increment.read()
.expect("PrimaryKey auto_increment lock poisoned")
}
pub fn len(&self) -> usize {
self.btree.len()
}
pub fn is_empty(&self) -> bool {
self.btree.is_empty()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn flush(&self) -> Result<()> {
self.btree.flush()
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn create_test_index() -> (PrimaryKeyIndex, TempDir) {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path().join("pk.index");
let index = PrimaryKeyIndex::new("users", path).unwrap();
(index, temp_dir)
}
#[test]
fn test_primary_key_insert() {
let (mut pk_index, _temp) = create_test_index();
pk_index.insert(1, 100).unwrap();
pk_index.insert(2, 200).unwrap();
assert_eq!(pk_index.get(1).unwrap(), Some(100));
assert_eq!(pk_index.get(2).unwrap(), Some(200));
assert_eq!(pk_index.len(), 2);
}
#[test]
fn test_auto_increment() {
let (mut pk_index, _temp) = create_test_index();
let pk1 = pk_index.insert_auto(100).unwrap();
let pk2 = pk_index.insert_auto(200).unwrap();
let pk3 = pk_index.insert_auto(300).unwrap();
assert_eq!(pk1, 1);
assert_eq!(pk2, 2);
assert_eq!(pk3, 3);
assert_eq!(pk_index.next_auto_increment(), 4);
}
#[test]
fn test_unique_constraint() {
let (mut pk_index, _temp) = create_test_index();
pk_index.insert(1, 100).unwrap();
let result = pk_index.insert(1, 200);
assert!(result.is_ok());
assert_eq!(pk_index.get(1).unwrap(), Some(200));
}
#[test]
fn test_update() {
let (mut pk_index, _temp) = create_test_index();
pk_index.insert(1, 100).unwrap();
pk_index.update(1, 999).unwrap();
assert_eq!(pk_index.get(1).unwrap(), Some(999));
}
#[test]
fn test_delete() {
let (mut pk_index, _temp) = create_test_index();
pk_index.insert(1, 100).unwrap();
pk_index.insert(2, 200).unwrap();
assert!(pk_index.delete(1).unwrap());
assert_eq!(pk_index.len(), 1);
assert!(!pk_index.exists(1).unwrap());
}
#[test]
fn test_range_query() {
let (mut pk_index, _temp) = create_test_index();
for i in 1..=10 {
pk_index.insert(i, i * 100).unwrap();
}
let results = pk_index.range(3, 7).unwrap();
assert_eq!(results.len(), 5);
assert_eq!(results[0], (3, 300));
assert_eq!(results[4], (7, 700));
}
#[test]
fn test_min_max() {
let (mut pk_index, _temp) = create_test_index();
pk_index.insert(5, 50).unwrap();
pk_index.insert(1, 10).unwrap();
pk_index.insert(10, 100).unwrap();
assert_eq!(pk_index.min_pk().unwrap(), Some(1));
assert_eq!(pk_index.max_pk().unwrap(), Some(10));
}
#[test]
fn test_persistence() {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path().join("persist.index");
{
let mut index = PrimaryKeyIndex::new("users", path.clone()).unwrap();
index.insert(1, 100).unwrap();
index.insert(2, 200).unwrap();
index.flush().unwrap();
}
{
let index = PrimaryKeyIndex::new("users", path).unwrap();
assert_eq!(index.get(1).unwrap(), Some(100));
assert_eq!(index.get(2).unwrap(), Some(200));
}
}
}