1use super::{Commitment, TableCommitment};
2use crate::base::{
3 database::{
4 ColumnField, ColumnRef, ColumnType, CommitmentAccessor, MetadataAccessor, SchemaAccessor,
5 TableRef,
6 },
7 map::IndexMap,
8};
9use alloc::vec::Vec;
10use sqlparser::ast::Ident;
11
12pub type QueryCommitments<C> = IndexMap<TableRef, TableCommitment<C>>;
19
20pub trait QueryCommitmentsExt<C>
22where
23 C: Commitment,
24{
25 fn from_accessor_with_max_bounds(
27 columns: impl IntoIterator<Item = ColumnRef>,
28 accessor: &(impl CommitmentAccessor<C> + SchemaAccessor),
29 ) -> Self;
30}
31
32impl<C: Commitment> QueryCommitmentsExt<C> for QueryCommitments<C> {
33 fn from_accessor_with_max_bounds(
34 columns: impl IntoIterator<Item = ColumnRef>,
35 accessor: &(impl CommitmentAccessor<C> + SchemaAccessor),
36 ) -> Self {
37 columns
38 .into_iter()
39 .fold(
40 IndexMap::<_, Vec<_>>::default(),
41 |mut table_columns, column| {
42 table_columns
43 .entry(column.table_ref())
44 .or_default()
45 .push(ColumnField::new(column.column_id(), *column.column_type()));
46 table_columns
47 },
48 )
49 .into_iter()
50 .map(|(table_ref, column_fields)| {
51 let selected_column_fields = accessor
52 .lookup_schema(table_ref.clone())
53 .into_iter()
54 .filter_map(|(ident, _)| {
55 column_fields
56 .iter()
57 .find(|column_field| column_field.name() == ident)
58 .cloned()
59 })
60 .collect::<Vec<_>>();
61 let table_commitment = TableCommitment::from_accessor_with_max_bounds(
62 &table_ref,
63 &selected_column_fields,
64 accessor,
65 );
66 (table_ref, table_commitment)
67 })
68 .collect()
69 }
70}
71
72impl<C: Commitment> MetadataAccessor for QueryCommitments<C> {
73 fn get_length(&self, table_ref: &TableRef) -> usize {
74 let table_commitment = self.get(&table_ref).unwrap();
75 table_commitment.num_rows()
76 }
77
78 fn get_offset(&self, table_ref: &TableRef) -> usize {
79 let table_commitment = self.get(&table_ref).unwrap();
80 table_commitment.range().start
81 }
82}
83
84impl<C: Commitment> CommitmentAccessor<C> for QueryCommitments<C> {
88 fn get_commitment(&self, column: ColumnRef) -> C {
89 let table_commitment = self.get(&column.table_ref()).unwrap();
90
91 table_commitment
92 .column_commitments()
93 .get_commitment(&column.column_id())
94 .unwrap()
95 }
96}
97
98impl<C: Commitment> SchemaAccessor for QueryCommitments<C> {
99 fn lookup_column(
100 &self,
101 table_ref: crate::base::database::TableRef,
102 column_id: Ident,
103 ) -> Option<ColumnType> {
104 let table_commitment = self.get(&table_ref)?;
105
106 table_commitment
107 .column_commitments()
108 .get_metadata(&column_id)
109 .map(|column_metadata| *column_metadata.column_type())
110 }
111
112 fn lookup_schema(
116 &self,
117 table_ref: crate::base::database::TableRef,
118 ) -> Vec<(Ident, ColumnType)> {
119 let table_commitment = self.get(&table_ref).unwrap();
120
121 table_commitment
122 .column_commitments()
123 .column_metadata()
124 .iter()
125 .map(|(identifier, column_metadata)| {
126 (identifier.clone(), *column_metadata.column_type())
127 })
128 .collect()
129 }
130}
131
132#[cfg(all(test, feature = "blitzar"))]
133mod tests {
134 use super::*;
135 use crate::{
136 base::{
137 commitment::{naive_commitment::NaiveCommitment, Bounds, ColumnBounds},
138 database::{
139 owned_table_utility::*, OwnedColumn, OwnedTable, OwnedTableTestAccessor,
140 TestAccessor,
141 },
142 scalar::test_scalar::TestScalar,
143 },
144 proof_primitive::dory::{
145 test_rng, DoryCommitment, DoryEvaluationProof, DoryProverPublicSetup, ProverSetup,
146 PublicParameters,
147 },
148 };
149
150 #[test]
151 fn we_can_get_length_and_offset_of_tables() {
152 let table_a: OwnedTable<TestScalar> = owned_table([
153 bigint("column_a", [1, 2, 3, 4]),
154 varchar("column_b", ["Lorem", "ipsum", "dolor", "sit"]),
155 ]);
156
157 let table_b: OwnedTable<TestScalar> = owned_table([scalar("column_c", [1, 2])]);
158
159 let offset_commitment =
160 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
161 let offset_table_id = TableRef::new("off", "table");
162
163 let no_offset_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
164 let no_offset_id = TableRef::new("no", "off");
165
166 let no_columns_commitment = TableCommitment::try_from_columns_with_offset(
167 Vec::<(&Ident, &OwnedColumn<TestScalar>)>::new(),
168 0,
169 &(),
170 )
171 .unwrap();
172 let no_columns_id = TableRef::new("no", "columns");
173
174 let no_rows_commitment = TableCommitment::try_from_columns_with_offset(
175 [(
176 &"column_c".into(),
177 &OwnedColumn::<TestScalar>::BigInt(vec![]),
178 )],
179 3,
180 &(),
181 )
182 .unwrap();
183 let no_rows_id = TableRef::new("no", "rows");
184
185 let query_commitments = QueryCommitments::from_iter([
186 (offset_table_id.clone(), offset_commitment),
187 (no_offset_id.clone(), no_offset_commitment),
188 (no_columns_id.clone(), no_columns_commitment),
189 (no_rows_id.clone(), no_rows_commitment),
190 ]);
191
192 assert_eq!(query_commitments.get_offset(&offset_table_id), 2);
193 assert_eq!(query_commitments.get_length(&offset_table_id), 4);
194
195 assert_eq!(query_commitments.get_offset(&no_offset_id), 0);
196 assert_eq!(query_commitments.get_length(&no_offset_id), 2);
197
198 assert_eq!(query_commitments.get_offset(&no_columns_id), 0);
199 assert_eq!(query_commitments.get_length(&no_columns_id), 0);
200
201 assert_eq!(query_commitments.get_offset(&no_rows_id), 3);
202 assert_eq!(query_commitments.get_length(&no_rows_id), 0);
203 }
204
205 #[expect(clippy::similar_names)]
206 #[test]
207 fn we_can_get_commitment_of_a_column() {
208 let column_a_id: Ident = "column_a".into();
209 let column_b_id: Ident = "column_b".into();
210
211 let table_a: OwnedTable<TestScalar> = owned_table([
212 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
213 varchar(
214 column_b_id.value.as_str(),
215 ["Lorem", "ipsum", "dolor", "sit"],
216 ),
217 ]);
218 let table_b: OwnedTable<TestScalar> =
219 owned_table([scalar(column_a_id.value.as_str(), [1, 2])]);
220
221 let table_a_commitment =
222 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
223 let table_a_id = TableRef::new("table", "a");
224
225 let table_b_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
226 let table_b_id = TableRef::new("table", "b");
227
228 let query_commitments = QueryCommitments::from_iter([
229 (table_a_id.clone(), table_a_commitment.clone()),
230 (table_b_id.clone(), table_b_commitment.clone()),
231 ]);
232
233 assert_eq!(
234 query_commitments.get_commitment(ColumnRef::new(
235 table_a_id.clone(),
236 column_a_id.clone(),
237 ColumnType::BigInt
238 )),
239 table_a_commitment.column_commitments().commitments()[0]
240 );
241 assert_eq!(
242 query_commitments.get_commitment(ColumnRef::new(
243 table_a_id,
244 column_b_id,
245 ColumnType::VarChar
246 )),
247 table_a_commitment.column_commitments().commitments()[1]
248 );
249 assert_eq!(
250 query_commitments.get_commitment(ColumnRef::new(
251 table_b_id,
252 column_a_id,
253 ColumnType::Scalar
254 )),
255 table_b_commitment.column_commitments().commitments()[0]
256 );
257 }
258
259 #[expect(clippy::similar_names)]
260 #[test]
261 fn we_can_get_schema_of_tables() {
262 let column_a_id: Ident = "column_a".into();
263 let column_b_id: Ident = "column_b".into();
264
265 let table_a: OwnedTable<TestScalar> = owned_table([
266 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
267 varchar(
268 column_b_id.value.as_str(),
269 ["Lorem", "ipsum", "dolor", "sit"],
270 ),
271 ]);
272 let table_b: OwnedTable<TestScalar> =
273 owned_table([scalar(column_a_id.value.as_str(), [1, 2])]);
274
275 let table_a_commitment =
276 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
277 let table_a_id = TableRef::new("table", "a");
278
279 let table_b_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
280 let table_b_id = TableRef::new("table", "b");
281
282 let no_columns_commitment = TableCommitment::try_from_columns_with_offset(
283 Vec::<(&Ident, &OwnedColumn<TestScalar>)>::new(),
284 0,
285 &(),
286 )
287 .unwrap();
288 let no_columns_id = TableRef::new("no", "columns");
289
290 let query_commitments = QueryCommitments::from_iter([
291 (table_a_id.clone(), table_a_commitment),
292 (table_b_id.clone(), table_b_commitment),
293 (no_columns_id.clone(), no_columns_commitment),
294 ]);
295
296 assert_eq!(
297 query_commitments
298 .lookup_column(table_a_id.clone(), column_a_id.clone())
299 .unwrap(),
300 ColumnType::BigInt
301 );
302 assert_eq!(
303 query_commitments
304 .lookup_column(table_a_id.clone(), column_b_id.clone())
305 .unwrap(),
306 ColumnType::VarChar
307 );
308 assert_eq!(
309 query_commitments.lookup_schema(table_a_id),
310 vec![
311 (column_a_id.clone(), ColumnType::BigInt),
312 (column_b_id.clone(), ColumnType::VarChar)
313 ]
314 );
315
316 assert_eq!(
317 query_commitments
318 .lookup_column(table_b_id.clone(), column_a_id.clone())
319 .unwrap(),
320 ColumnType::Scalar
321 );
322 assert_eq!(
323 query_commitments.lookup_column(table_b_id.clone(), column_b_id),
324 None
325 );
326 assert_eq!(
327 query_commitments.lookup_schema(table_b_id),
328 vec![(column_a_id.clone(), ColumnType::Scalar),]
329 );
330
331 assert_eq!(
332 query_commitments.lookup_column(no_columns_id.clone(), column_a_id),
333 None
334 );
335 assert_eq!(query_commitments.lookup_schema(no_columns_id), vec![]);
336 }
337
338 #[expect(clippy::similar_names)]
339 #[test]
340 fn we_can_get_query_commitments_from_accessor() {
341 let public_parameters = PublicParameters::test_rand(4, &mut test_rng());
342 let prover_setup = ProverSetup::from(&public_parameters);
343 let setup = DoryProverPublicSetup::new(&prover_setup, 3);
344
345 let column_a_id: Ident = "column_a".into();
346 let column_b_id: Ident = "column_b".into();
347
348 let table_a = owned_table([
349 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
350 varchar(
351 column_b_id.value.as_str(),
352 ["Lorem", "ipsum", "dolor", "sit"],
353 ),
354 ]);
355 let table_b = owned_table([
356 scalar(column_a_id.value.as_str(), [1, 2]),
357 int128(column_b_id.value.as_str(), [1, 2]),
358 ]);
359
360 let mut table_a_commitment =
361 TableCommitment::from_owned_table_with_offset(&table_a, 0, &setup);
362 let table_a_id = TableRef::new("table", "a");
363 *table_a_commitment
364 .column_commitments_mut()
365 .column_metadata_mut()
366 .get_mut(&column_a_id)
367 .unwrap()
368 .bounds_mut() = ColumnBounds::BigInt(Bounds::bounded(i64::MIN, i64::MAX).unwrap());
369
370 let mut table_b_commitment =
371 TableCommitment::from_owned_table_with_offset(&table_b, 0, &setup);
372 let table_b_id = TableRef::new("table", "b");
373 *table_b_commitment
374 .column_commitments_mut()
375 .column_metadata_mut()
376 .get_mut(&column_b_id)
377 .unwrap()
378 .bounds_mut() = ColumnBounds::Int128(Bounds::bounded(i128::MIN, i128::MAX).unwrap());
379
380 let expected_query_commitments = QueryCommitments::from_iter([
381 (table_a_id.clone(), table_a_commitment.clone()),
382 (table_b_id.clone(), table_b_commitment.clone()),
383 ]);
384
385 let mut accessor =
386 OwnedTableTestAccessor::<DoryEvaluationProof>::new_empty_with_setup(setup);
387 accessor.add_table(table_a_id.clone(), table_a, 0);
388 accessor.add_table(table_b_id.clone(), table_b, 0);
389
390 let query_commitments = QueryCommitments::<DoryCommitment>::from_accessor_with_max_bounds(
391 [
392 ColumnRef::new(table_a_id.clone(), column_a_id.clone(), ColumnType::BigInt),
393 ColumnRef::new(table_b_id.clone(), column_a_id, ColumnType::Scalar),
394 ColumnRef::new(table_a_id, column_b_id.clone(), ColumnType::VarChar),
395 ColumnRef::new(table_b_id, column_b_id, ColumnType::Int128),
396 ],
397 &accessor,
398 );
399 assert_eq!(query_commitments, expected_query_commitments);
400 }
401}