spacetimedb_vm/
relation.rs

1use core::hash::{Hash, Hasher};
2use spacetimedb_execution::Row;
3use spacetimedb_lib::db::auth::StAccess;
4use spacetimedb_sats::bsatn::{ser::BsatnError, ToBsatn};
5use spacetimedb_sats::product_value::ProductValue;
6use spacetimedb_sats::{impl_serialize, AlgebraicValue};
7use spacetimedb_schema::relation::{ColExpr, ColExprRef, Header};
8use spacetimedb_table::read_column::ReadColumn;
9use spacetimedb_table::table::RowRef;
10use std::borrow::Cow;
11use std::sync::Arc;
12
13/// RelValue represents either a reference to a row in a table,
14/// a reference to an inserted row,
15/// or an ephemeral row constructed during query execution.
16///
17/// A `RelValue` is the type generated/consumed by queries.
18#[derive(Debug, Clone)]
19pub enum RelValue<'a> {
20    /// A reference to a row in a table.
21    Row(RowRef<'a>),
22    /// An ephemeral row made during query execution.
23    Projection(ProductValue),
24    /// A row coming directly from a collected update.
25    ///
26    /// This is really a row in a table, and not an actual projection.
27    /// However, for (lifetime) reasons, we cannot (yet) keep it as a `RowRef<'_>`
28    /// and must convert that into a `ProductValue`.
29    ProjRef(&'a ProductValue),
30}
31
32impl<'a> From<Row<'a>> for RelValue<'a> {
33    fn from(value: Row<'a>) -> Self {
34        match value {
35            Row::Ptr(ptr) => Self::Row(ptr),
36            Row::Ref(ptr) => Self::ProjRef(ptr),
37        }
38    }
39}
40
41impl Eq for RelValue<'_> {}
42
43impl PartialEq for RelValue<'_> {
44    fn eq(&self, other: &Self) -> bool {
45        match (self, other) {
46            (Self::Projection(x), Self::Projection(y)) => x == y,
47            (Self::ProjRef(x), Self::ProjRef(y)) => x == y,
48            (Self::Row(x), Self::Row(y)) => x == y,
49            (Self::Projection(x), Self::ProjRef(y)) | (Self::ProjRef(y), Self::Projection(x)) => x == *y,
50            (Self::Row(x), Self::Projection(y)) | (Self::Projection(y), Self::Row(x)) => x == y,
51            (Self::Row(x), Self::ProjRef(y)) | (Self::ProjRef(y), Self::Row(x)) => x == *y,
52        }
53    }
54}
55
56impl Hash for RelValue<'_> {
57    fn hash<H: Hasher>(&self, state: &mut H) {
58        match self {
59            // `x.hash(state)` and `x.to_product_value().hash(state)`
60            // have the same effect on `state`.
61            Self::Row(x) => x.hash(state),
62            Self::Projection(x) => x.hash(state),
63            Self::ProjRef(x) => x.hash(state),
64        }
65    }
66}
67
68impl_serialize!(['a] RelValue<'a>, (self, ser) => match self {
69    Self::Row(row) => row.serialize(ser),
70    Self::Projection(row) => row.serialize(ser),
71    Self::ProjRef(row) => row.serialize(ser),
72});
73
74impl<'a> RelValue<'a> {
75    /// Converts `self` into a [`ProductValue`]
76    /// either by reading a value from a table,
77    /// cloning the reference to a `ProductValue`,
78    /// or consuming the owned product.
79    pub fn into_product_value(self) -> ProductValue {
80        match self {
81            Self::Row(row) => row.to_product_value(),
82            Self::Projection(row) => row,
83            Self::ProjRef(row) => row.clone(),
84        }
85    }
86
87    /// Converts `self` into a `Cow<'a, ProductValue>`
88    /// either by reading a value from a table,
89    /// passing the reference to a `ProductValue`,
90    /// or consuming the owned product.
91    pub fn into_product_value_cow(self) -> Cow<'a, ProductValue> {
92        match self {
93            Self::Row(row) => Cow::Owned(row.to_product_value()),
94            Self::Projection(row) => Cow::Owned(row),
95            Self::ProjRef(row) => Cow::Borrowed(row),
96        }
97    }
98
99    /// Computes the number of columns in this value.
100    pub fn num_columns(&self) -> usize {
101        match self {
102            Self::Row(row_ref) => row_ref.row_layout().product().elements.len(),
103            Self::Projection(row) => row.elements.len(),
104            Self::ProjRef(row) => row.elements.len(),
105        }
106    }
107
108    /// Extends `self` with the columns in `other`.
109    ///
110    /// This will always cause `RowRef<'_>`s to be read out into [`ProductValue`]s.
111    pub fn extend(self, other: RelValue<'a>) -> RelValue<'a> {
112        let mut x: Vec<_> = self.into_product_value().elements.into();
113        x.extend(other.into_product_value());
114        RelValue::Projection(x.into())
115    }
116
117    /// Read the column at index `col`.
118    ///
119    /// Use `read_or_take_column` instead if you have ownership of `self`.
120    pub fn read_column(&self, col: usize) -> Option<Cow<'_, AlgebraicValue>> {
121        match self {
122            Self::Row(row_ref) => AlgebraicValue::read_column(*row_ref, col).ok().map(Cow::Owned),
123            Self::Projection(pv) => pv.elements.get(col).map(Cow::Borrowed),
124            Self::ProjRef(pv) => pv.elements.get(col).map(Cow::Borrowed),
125        }
126    }
127
128    /// Returns a column either at the index specified in `col`,
129    /// or the column is the value that `col` holds.
130    ///
131    /// Panics if, for `ColExprRef::Col(col)`, the `col` is out of bounds of `self`.
132    pub fn get(&'a self, col: ColExprRef<'a>) -> Cow<'a, AlgebraicValue> {
133        match col {
134            ColExprRef::Col(col) => self.read_column(col.idx()).unwrap(),
135            ColExprRef::Value(x) => Cow::Borrowed(x),
136        }
137    }
138
139    /// Reads or takes the column at `col`.
140    /// Calling this method consumes the column at `col` for a `RelValue::Projection`,
141    /// so it should not be called again for the same input.
142    ///
143    /// Panics if `col` is out of bounds of `self`.
144    pub fn read_or_take_column(&mut self, col: usize) -> Option<AlgebraicValue> {
145        match self {
146            Self::Row(row_ref) => AlgebraicValue::read_column(*row_ref, col).ok(),
147            Self::Projection(pv) => pv.elements.get_mut(col).map(AlgebraicValue::take),
148            Self::ProjRef(pv) => pv.elements.get(col).cloned(),
149        }
150    }
151
152    /// Turns `cols` into a product
153    /// where a value in `cols` is taken directly from it and indices are taken from `self`.
154    ///
155    /// Panics on an index that is out of bounds of `self`.
156    pub fn project_owned(mut self, cols: &[ColExpr]) -> ProductValue {
157        cols.iter()
158            .map(|col| match col {
159                ColExpr::Col(col) => self.read_or_take_column(col.idx()).unwrap(),
160                ColExpr::Value(x) => x.clone(),
161            })
162            .collect()
163    }
164}
165
166impl ToBsatn for RelValue<'_> {
167    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {
168        match self {
169            RelValue::Row(this) => this.to_bsatn_vec(),
170            RelValue::Projection(this) => this.to_bsatn_vec(),
171            RelValue::ProjRef(this) => (*this).to_bsatn_vec(),
172        }
173    }
174    fn to_bsatn_extend(&self, buf: &mut Vec<u8>) -> Result<(), BsatnError> {
175        match self {
176            RelValue::Row(this) => this.to_bsatn_extend(buf),
177            RelValue::Projection(this) => this.to_bsatn_extend(buf),
178            RelValue::ProjRef(this) => this.to_bsatn_extend(buf),
179        }
180    }
181    fn static_bsatn_size(&self) -> Option<u16> {
182        match self {
183            RelValue::Row(this) => this.static_bsatn_size(),
184            RelValue::Projection(this) => this.static_bsatn_size(),
185            RelValue::ProjRef(this) => this.static_bsatn_size(),
186        }
187    }
188}
189
190/// An in-memory table
191// TODO(perf): Remove `Clone` impl.
192#[derive(Debug, Clone, Eq, PartialEq)]
193pub struct MemTable {
194    pub head: Arc<Header>,
195    pub data: Vec<ProductValue>,
196    pub table_access: StAccess,
197}
198
199impl MemTable {
200    pub fn new(head: Arc<Header>, table_access: StAccess, data: Vec<ProductValue>) -> Self {
201        assert_eq!(
202            head.fields.len(),
203            data.first()
204                .map(|pv| pv.elements.len())
205                .unwrap_or_else(|| head.fields.len()),
206            "number of columns in `header.len() != data.len()`"
207        );
208        Self {
209            head,
210            data,
211            table_access,
212        }
213    }
214
215    pub fn from_iter(head: Arc<Header>, data: impl IntoIterator<Item = ProductValue>) -> Self {
216        Self {
217            head,
218            data: data.into_iter().collect(),
219            table_access: StAccess::Public,
220        }
221    }
222}