proof_of_sql/base/database/
accessor.rs

1use crate::base::{
2    commitment::Commitment,
3    database::{Column, ColumnType, Table, TableOptions, TableRef},
4    map::{IndexMap, IndexSet},
5    scalar::Scalar,
6};
7use alloc::vec::Vec;
8use sqlparser::ast::Ident;
9
10/// Access metadata of a table span in a database.
11///
12/// Both Prover and Verifier use this information when processing a query.
13///
14/// Note: we assume that the query has already been validated so that we
15/// will only be accessing information about tables that exist in the database.
16pub trait MetadataAccessor {
17    /// Return the data span's length in the table (not the full table length)
18    fn get_length(&self, table_ref: &TableRef) -> usize;
19
20    /// Return the data span's offset in the table
21    ///
22    /// If the data span has its first row starting at the ith table row,
23    /// this `get_offset` should then return `i`.
24    fn get_offset(&self, table_ref: &TableRef) -> usize;
25}
26
27/// Access commitments of database columns.
28///
29/// Verifier uses this information to process a query.
30///
31/// In pseudo-code, here is a sketch of how [`CommitmentAccessor`] fits in
32/// with the verification workflow:
33///
34/// ```ignore
35/// verify(proof, query, commitment_database) {
36///     if(!validate_query(query, commitment_database)) {
37///         // if the query references columns that don't exist
38///         // we should error here before going any further
39///         return invalid-query()
40///     }
41///     commitment_database.reader_lock()
42///     // we can't be updating commitments while verifying
43///     accessor <- make-commitment-accessor(commitment_database)
44///     verify_result <- verify-valid-query(proof, query, accessor)
45///     commitment_database.reader_unlock()
46///     return verify_result
47/// }
48/// ```
49///
50/// Note: we assume that the query has already been validated so that we
51/// will only be accessing information about columns that exist in the database.
52pub trait CommitmentAccessor<C: Commitment>: MetadataAccessor {
53    /// Return the full table column commitment
54    fn get_commitment(&self, table_ref: &TableRef, column_id: &Ident) -> C;
55}
56
57/// Access database columns of an in-memory table span.
58///
59/// Prover uses this information to process a query.
60///
61/// In pseudo-code, here is a sketch of how [`DataAccessor`] fits in
62/// with the prove workflow:
63///
64/// ```ignore
65/// prove(query, database) {
66///       if(!validate_query(query, database)) {
67///           // if the query references columns that don't exist
68///           // we should error here before going any further
69///           invalid-query()
70///       }
71///       update-cached-columns(database, query)
72///            // if the database represents an in-memory cache of an externally persisted
73///            // database we should update the cache so that any column referenced in the query
74///            // will be available
75///       database.reader_lock()
76///           // we can't be updating the database while proving
77///       accessor <- make-data-accessor(database)
78///       proof <- prove-valid-query(query, accessor)
79///       database.reader_unlock()
80///       return proof
81/// }
82/// ```
83///
84/// Note: we assume that the query has already been validated so that we
85/// will only be accessing information about columns that exist in the database.
86pub trait DataAccessor<S: Scalar>: MetadataAccessor {
87    /// Return the data span in the table (not the full-table data)
88    fn get_column(&self, table_ref: &TableRef, column_id: &Ident) -> Column<S>;
89
90    /// Creates a new [`Table`] from a [`TableRef`] and [`Ident`]s.
91    ///
92    /// Columns are retrieved from the [`DataAccessor`] using the provided [`TableRef`] and [`Ident`]s.
93    /// The only reason why [`table_ref`] is needed is because [`column_ids`] can be empty.
94    /// # Panics
95    /// Column length mismatches can occur in theory. In practice, this should not happen.
96    fn get_table(&self, table_ref: &TableRef, column_ids: &IndexSet<Ident>) -> Table<S> {
97        if column_ids.is_empty() {
98            let input_length = self.get_length(table_ref);
99            Table::<S>::try_new_with_options(
100                IndexMap::default(),
101                TableOptions::new(Some(input_length)),
102            )
103        } else {
104            Table::<S>::try_from_iter(column_ids.into_iter().map(|column_id| {
105                let column = self.get_column(table_ref, column_id);
106                (column_id.clone(), column)
107            }))
108        }
109        .expect("Failed to create table from table and column references")
110    }
111}
112
113/// Access tables and their schemas in a database.
114///
115/// This accessor should be implemented by both the prover and verifier
116/// and then used by the Proof of SQL code to convert an `IntermediateAst`
117/// into a [`ProofPlan`](crate::sql::proof::ProofPlan).
118pub trait SchemaAccessor {
119    /// Lookup the column's data type in the specified table
120    ///
121    /// Return:
122    ///   - Some(type) if the column exists, where `type` is the column's data type
123    ///   - None in case the column does not exist in the table
124    ///
125    /// Precondition 1: the table must exist and be tamperproof.
126    /// Precondition 2: `table_ref` and `column_id` must always be lowercase.
127    fn lookup_column(&self, table_ref: &TableRef, column_id: &Ident) -> Option<ColumnType>;
128
129    /// Lookup all the column names and their data types in the specified table
130    ///
131    /// Return:
132    ///   - The list of column names with their data types
133    ///
134    /// Precondition 1: the table must exist and be tamperproof.
135    /// Precondition 2: `table_name` must be lowercase.
136    fn lookup_schema(&self, table_ref: &TableRef) -> Vec<(Ident, ColumnType)>;
137}