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)
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, table_ref: &TableRef, column_id: &Ident) -> C {
89 let table_commitment = self.get(table_ref).unwrap();
90
91 table_commitment
92 .column_commitments()
93 .get_commitment(column_id)
94 .unwrap()
95 }
96}
97
98impl<C: Commitment> SchemaAccessor for QueryCommitments<C> {
99 fn lookup_column(&self, table_ref: &TableRef, column_id: &Ident) -> Option<ColumnType> {
100 let table_commitment = self.get(table_ref)?;
101
102 table_commitment
103 .column_commitments()
104 .get_metadata(column_id)
105 .map(|column_metadata| *column_metadata.column_type())
106 }
107
108 fn lookup_schema(&self, table_ref: &TableRef) -> Vec<(Ident, ColumnType)> {
112 let table_commitment = self.get(table_ref).unwrap();
113
114 table_commitment
115 .column_commitments()
116 .column_metadata()
117 .iter()
118 .map(|(identifier, column_metadata)| {
119 (identifier.clone(), *column_metadata.column_type())
120 })
121 .collect()
122 }
123}
124
125#[cfg(all(test, feature = "blitzar"))]
126mod tests {
127 use super::*;
128 use crate::{
129 base::{
130 commitment::{naive_commitment::NaiveCommitment, Bounds, ColumnBounds},
131 database::{
132 owned_table_utility::*, OwnedColumn, OwnedTable, OwnedTableTestAccessor,
133 TestAccessor,
134 },
135 scalar::test_scalar::TestScalar,
136 },
137 proof_primitive::dory::{
138 test_rng, DoryCommitment, DoryEvaluationProof, DoryProverPublicSetup, ProverSetup,
139 PublicParameters,
140 },
141 };
142
143 #[test]
144 fn we_can_get_length_and_offset_of_tables() {
145 let table_a: OwnedTable<TestScalar> = owned_table([
146 bigint("column_a", [1, 2, 3, 4]),
147 varchar("column_b", ["Lorem", "ipsum", "dolor", "sit"]),
148 ]);
149
150 let table_b: OwnedTable<TestScalar> = owned_table([scalar("column_c", [1, 2])]);
151
152 let offset_commitment =
153 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
154 let offset_table_id = TableRef::new("off", "table");
155
156 let no_offset_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
157 let no_offset_id = TableRef::new("no", "off");
158
159 let no_columns_commitment = TableCommitment::try_from_columns_with_offset(
160 Vec::<(&Ident, &OwnedColumn<TestScalar>)>::new(),
161 0,
162 &(),
163 )
164 .unwrap();
165 let no_columns_id = TableRef::new("no", "columns");
166
167 let no_rows_commitment = TableCommitment::try_from_columns_with_offset(
168 [(
169 &"column_c".into(),
170 &OwnedColumn::<TestScalar>::BigInt(vec![]),
171 )],
172 3,
173 &(),
174 )
175 .unwrap();
176 let no_rows_id = TableRef::new("no", "rows");
177
178 let query_commitments = QueryCommitments::from_iter([
179 (offset_table_id.clone(), offset_commitment),
180 (no_offset_id.clone(), no_offset_commitment),
181 (no_columns_id.clone(), no_columns_commitment),
182 (no_rows_id.clone(), no_rows_commitment),
183 ]);
184
185 assert_eq!(query_commitments.get_offset(&offset_table_id), 2);
186 assert_eq!(query_commitments.get_length(&offset_table_id), 4);
187
188 assert_eq!(query_commitments.get_offset(&no_offset_id), 0);
189 assert_eq!(query_commitments.get_length(&no_offset_id), 2);
190
191 assert_eq!(query_commitments.get_offset(&no_columns_id), 0);
192 assert_eq!(query_commitments.get_length(&no_columns_id), 0);
193
194 assert_eq!(query_commitments.get_offset(&no_rows_id), 3);
195 assert_eq!(query_commitments.get_length(&no_rows_id), 0);
196 }
197
198 #[expect(clippy::similar_names)]
199 #[test]
200 fn we_can_get_commitment_of_a_column() {
201 let column_a_id: Ident = "column_a".into();
202 let column_b_id: Ident = "column_b".into();
203
204 let table_a: OwnedTable<TestScalar> = owned_table([
205 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
206 varchar(
207 column_b_id.value.as_str(),
208 ["Lorem", "ipsum", "dolor", "sit"],
209 ),
210 ]);
211 let table_b: OwnedTable<TestScalar> =
212 owned_table([scalar(column_a_id.value.as_str(), [1, 2])]);
213
214 let table_a_commitment =
215 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
216 let table_a_id = TableRef::new("table", "a");
217
218 let table_b_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
219 let table_b_id = TableRef::new("table", "b");
220
221 let query_commitments = QueryCommitments::from_iter([
222 (table_a_id.clone(), table_a_commitment.clone()),
223 (table_b_id.clone(), table_b_commitment.clone()),
224 ]);
225
226 assert_eq!(
227 query_commitments.get_commitment(&table_a_id, &column_a_id,),
228 table_a_commitment.column_commitments().commitments()[0]
229 );
230 assert_eq!(
231 query_commitments.get_commitment(&table_a_id, &column_b_id,),
232 table_a_commitment.column_commitments().commitments()[1]
233 );
234 assert_eq!(
235 query_commitments.get_commitment(&table_b_id, &column_a_id,),
236 table_b_commitment.column_commitments().commitments()[0]
237 );
238 }
239
240 #[expect(clippy::similar_names)]
241 #[test]
242 fn we_can_get_schema_of_tables() {
243 let column_a_id: Ident = "column_a".into();
244 let column_b_id: Ident = "column_b".into();
245
246 let table_a: OwnedTable<TestScalar> = owned_table([
247 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
248 varchar(
249 column_b_id.value.as_str(),
250 ["Lorem", "ipsum", "dolor", "sit"],
251 ),
252 ]);
253 let table_b: OwnedTable<TestScalar> =
254 owned_table([scalar(column_a_id.value.as_str(), [1, 2])]);
255
256 let table_a_commitment =
257 TableCommitment::<NaiveCommitment>::from_owned_table_with_offset(&table_a, 2, &());
258 let table_a_id = TableRef::new("table", "a");
259
260 let table_b_commitment = TableCommitment::from_owned_table_with_offset(&table_b, 0, &());
261 let table_b_id = TableRef::new("table", "b");
262
263 let no_columns_commitment = TableCommitment::try_from_columns_with_offset(
264 Vec::<(&Ident, &OwnedColumn<TestScalar>)>::new(),
265 0,
266 &(),
267 )
268 .unwrap();
269 let no_columns_id = TableRef::new("no", "columns");
270
271 let query_commitments = QueryCommitments::from_iter([
272 (table_a_id.clone(), table_a_commitment),
273 (table_b_id.clone(), table_b_commitment),
274 (no_columns_id.clone(), no_columns_commitment),
275 ]);
276
277 assert_eq!(
278 query_commitments
279 .lookup_column(&table_a_id, &column_a_id)
280 .unwrap(),
281 ColumnType::BigInt
282 );
283 assert_eq!(
284 query_commitments
285 .lookup_column(&table_a_id, &column_b_id)
286 .unwrap(),
287 ColumnType::VarChar
288 );
289 assert_eq!(
290 query_commitments.lookup_schema(&table_a_id),
291 vec![
292 (column_a_id.clone(), ColumnType::BigInt),
293 (column_b_id.clone(), ColumnType::VarChar)
294 ]
295 );
296
297 assert_eq!(
298 query_commitments
299 .lookup_column(&table_b_id, &column_a_id)
300 .unwrap(),
301 ColumnType::Scalar
302 );
303 assert_eq!(
304 query_commitments.lookup_column(&table_b_id, &column_b_id),
305 None
306 );
307 assert_eq!(
308 query_commitments.lookup_schema(&table_b_id),
309 vec![(column_a_id.clone(), ColumnType::Scalar),]
310 );
311
312 assert_eq!(
313 query_commitments.lookup_column(&no_columns_id, &column_a_id),
314 None
315 );
316 assert_eq!(query_commitments.lookup_schema(&no_columns_id), vec![]);
317 }
318
319 #[expect(clippy::similar_names)]
320 #[test]
321 fn we_can_get_query_commitments_from_accessor() {
322 let public_parameters = PublicParameters::test_rand(4, &mut test_rng());
323 let prover_setup = ProverSetup::from(&public_parameters);
324 let setup = DoryProverPublicSetup::new(&prover_setup, 3);
325
326 let column_a_id: Ident = "column_a".into();
327 let column_b_id: Ident = "column_b".into();
328
329 let table_a = owned_table([
330 bigint(column_a_id.value.as_str(), [1, 2, 3, 4]),
331 varchar(
332 column_b_id.value.as_str(),
333 ["Lorem", "ipsum", "dolor", "sit"],
334 ),
335 ]);
336 let table_b = owned_table([
337 scalar(column_a_id.value.as_str(), [1, 2]),
338 int128(column_b_id.value.as_str(), [1, 2]),
339 ]);
340
341 let mut table_a_commitment =
342 TableCommitment::from_owned_table_with_offset(&table_a, 0, &setup);
343 let table_a_id = TableRef::new("table", "a");
344 *table_a_commitment
345 .column_commitments_mut()
346 .column_metadata_mut()
347 .get_mut(&column_a_id)
348 .unwrap()
349 .bounds_mut() = ColumnBounds::BigInt(Bounds::bounded(i64::MIN, i64::MAX).unwrap());
350
351 let mut table_b_commitment =
352 TableCommitment::from_owned_table_with_offset(&table_b, 0, &setup);
353 let table_b_id = TableRef::new("table", "b");
354 *table_b_commitment
355 .column_commitments_mut()
356 .column_metadata_mut()
357 .get_mut(&column_b_id)
358 .unwrap()
359 .bounds_mut() = ColumnBounds::Int128(Bounds::bounded(i128::MIN, i128::MAX).unwrap());
360
361 let expected_query_commitments = QueryCommitments::from_iter([
362 (table_a_id.clone(), table_a_commitment.clone()),
363 (table_b_id.clone(), table_b_commitment.clone()),
364 ]);
365
366 let mut accessor =
367 OwnedTableTestAccessor::<DoryEvaluationProof>::new_empty_with_setup(setup);
368 accessor.add_table(table_a_id.clone(), table_a, 0);
369 accessor.add_table(table_b_id.clone(), table_b, 0);
370
371 let query_commitments = QueryCommitments::<DoryCommitment>::from_accessor_with_max_bounds(
372 [
373 ColumnRef::new(table_a_id.clone(), column_a_id.clone(), ColumnType::BigInt),
374 ColumnRef::new(table_b_id.clone(), column_a_id, ColumnType::Scalar),
375 ColumnRef::new(table_a_id, column_b_id.clone(), ColumnType::VarChar),
376 ColumnRef::new(table_b_id, column_b_id, ColumnType::Int128),
377 ],
378 &accessor,
379 );
380 assert_eq!(query_commitments, expected_query_commitments);
381 }
382}