Skip to main content

tpt_archon_relational/executor/
value.rs

1//! The executor's row/table data model: [`Value`], [`Row`], [`Table`], and
2//! the [`ExecError`]/[`ResultSet`] types every execution path produces or
3//! consumes, plus [`literal_to_value`] for lifting a parsed literal into one.
4
5use alloc::string::String;
6use alloc::vec::Vec;
7
8use crate::parser::Literal;
9
10/// A single value in a row (integers, text, float, an embedding vector, or NULL).
11#[derive(Debug, Clone, PartialEq)]
12pub enum Value {
13    /// A 64-bit integer.
14    Int(i64),
15    /// A 32-bit floating-point number.
16    Float(f32),
17    /// A UTF-8 text value.
18    Text(String),
19    /// A fixed-width `f32` embedding vector (the `f32[]` column type).
20    Vector(Vec<f32>),
21    /// SQL `NULL`.
22    Null,
23}
24
25impl Eq for Value {}
26
27impl PartialOrd for Value {
28    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
29        Some(self.cmp(other))
30    }
31}
32
33impl Ord for Value {
34    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
35        match (self, other) {
36            (Value::Int(a), Value::Int(b)) => a.cmp(b),
37            (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
38            (Value::Text(a), Value::Text(b)) => a.cmp(b),
39            (Value::Vector(a), Value::Vector(b)) => a.len().cmp(&b.len()).then_with(|| {
40                a.iter()
41                    .zip(b.iter())
42                    .map(|(x, y)| x.to_bits().cmp(&y.to_bits()))
43                    .find(|o| *o != core::cmp::Ordering::Equal)
44                    .unwrap_or(core::cmp::Ordering::Equal)
45            }),
46            (Value::Null, Value::Null) => core::cmp::Ordering::Equal,
47            (Value::Null, _) => core::cmp::Ordering::Greater,
48            (_, Value::Null) => core::cmp::Ordering::Less,
49            (Value::Int(_), _) => core::cmp::Ordering::Less,
50            (_, Value::Int(_)) => core::cmp::Ordering::Greater,
51            (Value::Float(_), Value::Text(_)) | (Value::Float(_), Value::Vector(_)) => {
52                core::cmp::Ordering::Less
53            }
54            (Value::Text(_), Value::Float(_)) | (Value::Vector(_), Value::Float(_)) => {
55                core::cmp::Ordering::Greater
56            }
57            (Value::Text(_), Value::Vector(_)) => core::cmp::Ordering::Less,
58            (Value::Vector(_), Value::Text(_)) => core::cmp::Ordering::Greater,
59        }
60    }
61}
62
63/// A row: values positionally aligned with the table's column names.
64pub type Row = Vec<Value>;
65
66/// A simple in-memory table.
67#[derive(Debug, Clone, Default)]
68pub struct Table {
69    /// Column names.
70    pub columns: Vec<String>,
71    /// Row data.
72    pub rows: Vec<Row>,
73}
74
75impl Table {
76    /// Creates a table with the given column names.
77    pub fn new(columns: Vec<String>) -> Self {
78        Self {
79            columns,
80            rows: Vec::new(),
81        }
82    }
83
84    /// Appends a row.
85    pub fn insert(&mut self, row: Row) {
86        self.rows.push(row);
87    }
88}
89
90/// Errors during execution.
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum ExecError {
93    /// A referenced column does not exist in the table.
94    UnknownColumn(String),
95    /// The predicate compared against a non-integer column.
96    TypeMismatch,
97    /// A GROUP BY column was not found.
98    GroupByColumnNotFound(String),
99    /// An `Expr::Exists`/`InSubquery`/`ScalarCmp` node reached the pure
100    /// evaluator. These require database access to run the inner query and
101    /// must be intercepted by `database::Database::eval_where` before
102    /// reaching here — this variant only guards against that invariant ever
103    /// being violated.
104    UnresolvedSubquery,
105}
106
107/// The result of running a query: output column names and rows.
108#[derive(Debug, Clone, PartialEq, Default)]
109pub struct ResultSet {
110    /// Output column names.
111    pub columns: Vec<String>,
112    /// Output rows.
113    pub rows: Vec<Row>,
114    /// Number of rows affected by a DML statement (`INSERT`/`UPDATE`/`DELETE`),
115    /// or `None` for queries (`SELECT`/`Compound`).
116    pub affected: Option<u64>,
117}
118
119/// A PostgreSQL-style command-completion tag, pairing the tag string with the
120/// row-count for DML statements.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub enum CommandTag {
123    Select(u64),
124    Insert(u64),
125    Update(u64),
126    Delete(u64),
127    CreateTable,
128    CreateView,
129    DropView,
130    AlterTable,
131    Begin,
132    Commit,
133    Rollback,
134    Set,
135    Reset,
136    Empty,
137}
138
139impl CommandTag {
140    /// Returns the tag string as PostgreSQL emits it in `CommandComplete`.
141    pub fn as_str(&self) -> &'static str {
142        match self {
143            CommandTag::Select(_) => "SELECT",
144            CommandTag::Insert(_) => "INSERT",
145            CommandTag::Update(_) => "UPDATE",
146            CommandTag::Delete(_) => "DELETE",
147            CommandTag::CreateTable => "CREATE TABLE",
148            CommandTag::CreateView => "CREATE VIEW",
149            CommandTag::DropView => "DROP VIEW",
150            CommandTag::AlterTable => "ALTER TABLE",
151            CommandTag::Begin => "BEGIN",
152            CommandTag::Commit => "COMMIT",
153            CommandTag::Rollback => "ROLLBACK",
154            CommandTag::Set => "SET",
155            CommandTag::Reset => "RESET",
156            CommandTag::Empty => "",
157        }
158    }
159
160    /// Returns the row-count suffix for DML tags, or `None` for DDL/txn tags.
161    pub fn row_count(&self) -> Option<u64> {
162        match self {
163            CommandTag::Select(n)
164            | CommandTag::Insert(n)
165            | CommandTag::Update(n)
166            | CommandTag::Delete(n) => Some(*n),
167            _ => None,
168        }
169    }
170}
171
172/// Converts a parser-level [`Literal`] into a runtime [`Value`].
173pub fn literal_to_value(lit: &Literal) -> Value {
174    match lit {
175        Literal::Int(v) => Value::Int(*v),
176        Literal::Float(v) => Value::Float(*v),
177        Literal::Text(s) => Value::Text(s.clone()),
178        Literal::Vector(v) => Value::Vector(v.clone()),
179        Literal::Null => Value::Null,
180    }
181}