aboutsummaryrefslogtreecommitdiff
path: root/database/src/table.rs
blob: eea7c3a25b3d94d685285296d5f0c019d6ede7ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use crate::{
    backends::{ReadTransaction, WriteTransaction},
    query::Query,
    sort::Index,
};
use anyhow::{Result, anyhow};
use jellyobject::ObjectBuffer;

pub type TableNum = u64;
pub type RowNum = u64;

pub struct Table {
    id: u32,
    pub(crate) indices: Vec<Box<dyn Index>>,
}
impl Table {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            indices: Vec::new(),
        }
    }
    fn key(&self, row: RowNum) -> Vec<u8> {
        let mut key = Vec::new();
        key.extend(self.id.to_be_bytes());
        key.extend(row.to_be_bytes());
        key
    }
    pub fn insert(&self, txn: &mut dyn WriteTransaction, entry: ObjectBuffer) -> Result<RowNum> {
        let mut id_counter = txn
            .get(&self.id.to_be_bytes())?
            .map(|k| k.as_slice().try_into().map(RowNum::from_be_bytes).ok())
            .flatten()
            .unwrap_or(0);
        let row = id_counter;
        id_counter += 1;
        txn.set(&self.id.to_be_bytes(), &id_counter.to_be_bytes())?;

        txn.set(&self.key(row), bytemuck::cast_slice(entry.0.as_slice()))?;

        let ob = entry.as_object();
        for idx in &self.indices {
            idx.add(txn, row, ob)?;
        }

        Ok(id_counter)
    }
    pub fn add_index<T: Index + Clone + 'static>(&mut self, index: T) -> T {
        self.indices.push(Box::new(index.clone()));
        index
    }
    pub fn get(&self, txn: &dyn ReadTransaction, row: RowNum) -> Result<Option<ObjectBuffer>> {
        Ok(txn.get(&self.key(row))?.map(ObjectBuffer::from))
    }
    pub fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum) -> Result<bool> {
        let Some(entry) = self.get(db, row)? else {
            return Ok(false);
        };
        let ob = entry.as_object();
        for idx in &self.indices {
            idx.remove(db, row, ob)?;
        }
        db.del(&self.key(row))?;
        Ok(true)
    }
    pub fn update(
        &self,
        txn: &mut dyn WriteTransaction,
        row: RowNum,
        entry: ObjectBuffer,
    ) -> Result<()> {
        let before = self.get(txn, row)?.ok_or(anyhow!("row to update missing"))?;
        let before = before.as_object();
        let after = entry.as_object();

        txn.set(&self.key(row), bytemuck::cast_slice(entry.0.as_slice()))?;

        for idx in &self.indices {
            if !idx.compare(before, after) {
                idx.remove(txn, row, before)?;
                idx.add(txn, row, after)?;
            }
        }

        Ok(())
    }
    pub fn query(
        &self,
        txn: &dyn ReadTransaction,
        query: Query,
    ) -> Box<dyn Iterator<Item = RowNum>> {
        todo!()
    }
    pub fn query_single(&self, txn: &dyn ReadTransaction, query: Query) -> Option<RowNum> {
        self.query(txn, query).next()
    }
}