proof_of_sql_planner/
expr.rs

1use super::{
2    column_to_column_ref, placeholder_to_placeholder_expr, scalar_value_to_literal_value,
3    PlannerError, PlannerResult,
4};
5use datafusion::logical_expr::{
6    expr::{Alias, Placeholder},
7    BinaryExpr, Expr, Operator,
8};
9use proof_of_sql::{
10    base::database::ColumnType,
11    sql::{proof_exprs::DynProofExpr, scale_cast_binary_op},
12};
13use sqlparser::ast::Ident;
14
15/// Convert a [`BinaryExpr`] to [`DynProofExpr`]
16#[expect(
17    clippy::missing_panics_doc,
18    reason = "Output of comparisons is always boolean"
19)]
20fn binary_expr_to_proof_expr(
21    left: &Expr,
22    right: &Expr,
23    op: Operator,
24    schema: &[(Ident, ColumnType)],
25) -> PlannerResult<DynProofExpr> {
26    let left_proof_expr = expr_to_proof_expr(left, schema)?;
27    let right_proof_expr = expr_to_proof_expr(right, schema)?;
28
29    let (left_proof_expr, right_proof_expr) = match op {
30        Operator::Eq
31        | Operator::NotEq
32        | Operator::Lt
33        | Operator::Gt
34        | Operator::LtEq
35        | Operator::GtEq
36        | Operator::Plus
37        | Operator::Minus => scale_cast_binary_op(left_proof_expr, right_proof_expr)?,
38        _ => (left_proof_expr, right_proof_expr),
39    };
40
41    match op {
42        Operator::And => Ok(DynProofExpr::try_new_and(
43            left_proof_expr,
44            right_proof_expr,
45        )?),
46        Operator::Or => Ok(DynProofExpr::try_new_or(left_proof_expr, right_proof_expr)?),
47        Operator::Multiply => Ok(DynProofExpr::try_new_multiply(
48            left_proof_expr,
49            right_proof_expr,
50        )?),
51        Operator::Eq => Ok(DynProofExpr::try_new_equals(
52            left_proof_expr,
53            right_proof_expr,
54        )?),
55        Operator::NotEq => Ok(DynProofExpr::try_new_not(DynProofExpr::try_new_equals(
56            left_proof_expr,
57            right_proof_expr,
58        )?)
59        .expect("An equality expression must have a boolean data type...")),
60        Operator::Lt => Ok(DynProofExpr::try_new_inequality(
61            left_proof_expr,
62            right_proof_expr,
63            true,
64        )?),
65        Operator::Gt => Ok(DynProofExpr::try_new_inequality(
66            left_proof_expr,
67            right_proof_expr,
68            false,
69        )?),
70        Operator::LtEq => Ok(DynProofExpr::try_new_not(DynProofExpr::try_new_inequality(
71            left_proof_expr,
72            right_proof_expr,
73            false,
74        )?)
75        .expect("An inequality expression must have a boolean data type...")),
76        Operator::GtEq => Ok(DynProofExpr::try_new_not(DynProofExpr::try_new_inequality(
77            left_proof_expr,
78            right_proof_expr,
79            true,
80        )?)
81        .expect("An inequality expression must have a boolean data type...")),
82        Operator::Plus => Ok(DynProofExpr::try_new_add(
83            left_proof_expr,
84            right_proof_expr,
85        )?),
86        Operator::Minus => Ok(DynProofExpr::try_new_subtract(
87            left_proof_expr,
88            right_proof_expr,
89        )?),
90        // Any other operator is unsupported
91        _ => Err(PlannerError::UnsupportedBinaryOperator { op }),
92    }
93}
94
95/// Convert an [`datafusion::expr::Expr`] to [`DynProofExpr`]
96///
97/// # Panics
98/// The function should not panic if Proof of SQL is working correctly
99pub fn expr_to_proof_expr(
100    expr: &Expr,
101    schema: &[(Ident, ColumnType)],
102) -> PlannerResult<DynProofExpr> {
103    match expr {
104        Expr::Alias(Alias { expr, .. }) => expr_to_proof_expr(expr, schema),
105        Expr::Column(col) => Ok(DynProofExpr::new_column(column_to_column_ref(col, schema)?)),
106        Expr::Placeholder(placeholder) => placeholder_to_placeholder_expr(placeholder),
107        Expr::BinaryExpr(BinaryExpr { left, right, op }) => {
108            binary_expr_to_proof_expr(left, right, *op, schema)
109        }
110        Expr::Literal(val) => Ok(DynProofExpr::new_literal(scalar_value_to_literal_value(
111            val.clone(),
112        )?)),
113        Expr::Not(expr) => {
114            let proof_expr = expr_to_proof_expr(expr, schema)?;
115            Ok(DynProofExpr::try_new_not(proof_expr)?)
116        }
117        Expr::Cast(cast) => {
118            match &*cast.expr {
119                // handle cases such as `$1::int`
120                Expr::Placeholder(placeholder) if placeholder.data_type.is_none() => {
121                    let typed_placeholder =
122                        Placeholder::new(placeholder.id.clone(), Some(cast.data_type.clone()));
123                    placeholder_to_placeholder_expr(&typed_placeholder)
124                }
125                _ => {
126                    let from_expr = expr_to_proof_expr(&cast.expr, schema)?;
127                    let to_type = cast.data_type.clone().try_into().map_err(|_| {
128                        PlannerError::UnsupportedDataType {
129                            data_type: cast.data_type.clone(),
130                        }
131                    })?;
132                    Ok(
133                        DynProofExpr::try_new_cast(from_expr.clone(), to_type).map_or_else(
134                            |_| DynProofExpr::try_new_scaling_cast(from_expr, to_type),
135                            Ok,
136                        )?,
137                    )
138                }
139            }
140        }
141        _ => Err(PlannerError::UnsupportedLogicalExpression {
142            expr: Box::new(expr.clone()),
143        }),
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use crate::df_util::*;
151    use arrow::datatypes::DataType;
152    use core::ops::{Add, Mul, Sub};
153    use datafusion::{
154        catalog::TableReference,
155        common::{Column, ScalarValue},
156        logical_expr::{expr::Placeholder, Cast},
157    };
158    use proof_of_sql::base::{
159        database::{ColumnRef, ColumnType, LiteralValue, TableRef},
160        math::decimal::Precision,
161    };
162
163    #[expect(non_snake_case)]
164    fn COLUMN_INT() -> DynProofExpr {
165        DynProofExpr::new_column(ColumnRef::new(
166            TableRef::from_names(Some("namespace"), "table_name"),
167            "column".into(),
168            ColumnType::Int,
169        ))
170    }
171
172    #[expect(non_snake_case)]
173    fn COLUMN1_SMALLINT() -> DynProofExpr {
174        DynProofExpr::new_column(ColumnRef::new(
175            TableRef::from_names(Some("namespace"), "table_name"),
176            "column1".into(),
177            ColumnType::SmallInt,
178        ))
179    }
180
181    #[expect(non_snake_case)]
182    fn COLUMN2_BIGINT() -> DynProofExpr {
183        DynProofExpr::new_column(ColumnRef::new(
184            TableRef::from_names(Some("namespace"), "table_name"),
185            "column2".into(),
186            ColumnType::BigInt,
187        ))
188    }
189
190    #[expect(non_snake_case)]
191    fn COLUMN1_BOOLEAN() -> DynProofExpr {
192        DynProofExpr::new_column(ColumnRef::new(
193            TableRef::from_names(Some("namespace"), "table_name"),
194            "column1".into(),
195            ColumnType::Boolean,
196        ))
197    }
198
199    #[expect(non_snake_case)]
200    fn COLUMN2_BOOLEAN() -> DynProofExpr {
201        DynProofExpr::new_column(ColumnRef::new(
202            TableRef::from_names(Some("namespace"), "table_name"),
203            "column2".into(),
204            ColumnType::Boolean,
205        ))
206    }
207
208    #[expect(non_snake_case)]
209    fn COLUMN3_DECIMAL_75_5() -> DynProofExpr {
210        DynProofExpr::new_column(ColumnRef::new(
211            TableRef::from_names(Some("namespace"), "table_name"),
212            "column3".into(),
213            ColumnType::Decimal75(
214                Precision::new(75).expect("Precision is definitely valid"),
215                5,
216            ),
217        ))
218    }
219
220    #[expect(non_snake_case)]
221    fn COLUMN2_DECIMAL_25_5() -> DynProofExpr {
222        DynProofExpr::new_column(ColumnRef::new(
223            TableRef::from_names(Some("namespace"), "table_name"),
224            "column2".into(),
225            ColumnType::Decimal75(
226                Precision::new(25).expect("Precision is definitely valid"),
227                5,
228            ),
229        ))
230    }
231
232    // Alias
233    #[test]
234    fn we_can_convert_alias_to_proof_expr() {
235        // Column
236        let expr = df_column("namespace.table_name", "column").alias("alias");
237        let schema = vec![("column".into(), ColumnType::Int)];
238        assert_eq!(expr_to_proof_expr(&expr, &schema).unwrap(), COLUMN_INT());
239    }
240
241    // Column
242    #[test]
243    fn we_can_convert_column_expr_to_proof_expr() {
244        // Column
245        let expr = df_column("namespace.table_name", "column");
246        let schema = vec![("column".into(), ColumnType::Int)];
247        assert_eq!(expr_to_proof_expr(&expr, &schema).unwrap(), COLUMN_INT());
248    }
249
250    // BinaryExpr
251    #[test]
252    fn we_can_convert_comparison_binary_expr_to_proof_expr() {
253        let schema = vec![
254            ("column1".into(), ColumnType::SmallInt),
255            ("column2".into(), ColumnType::BigInt),
256        ];
257
258        // Eq
259        let expr = df_column("namespace.table_name", "column1")
260            .eq(df_column("namespace.table_name", "column2"));
261        assert_eq!(
262            expr_to_proof_expr(&expr, &schema).unwrap(),
263            DynProofExpr::try_new_equals(COLUMN1_SMALLINT(), COLUMN2_BIGINT()).unwrap()
264        );
265
266        // Lt
267        let expr = df_column("namespace.table_name", "column1")
268            .lt(df_column("namespace.table_name", "column2"));
269        assert_eq!(
270            expr_to_proof_expr(&expr, &schema).unwrap(),
271            DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), true).unwrap()
272        );
273
274        // Gt
275        let expr = df_column("namespace.table_name", "column1")
276            .gt(df_column("namespace.table_name", "column2"));
277        assert_eq!(
278            expr_to_proof_expr(&expr, &schema).unwrap(),
279            DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), false).unwrap()
280        );
281
282        // LtEq
283        let expr = df_column("namespace.table_name", "column1")
284            .lt_eq(df_column("namespace.table_name", "column2"));
285        assert_eq!(
286            expr_to_proof_expr(&expr, &schema).unwrap(),
287            DynProofExpr::try_new_not(
288                DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), false)
289                    .unwrap()
290            )
291            .unwrap()
292        );
293
294        // GtEq
295        let expr = df_column("namespace.table_name", "column1")
296            .gt_eq(df_column("namespace.table_name", "column2"));
297        assert_eq!(
298            expr_to_proof_expr(&expr, &schema).unwrap(),
299            DynProofExpr::try_new_not(
300                DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), true)
301                    .unwrap()
302            )
303            .unwrap()
304        );
305    }
306
307    #[expect(clippy::too_many_lines)]
308    #[test]
309    fn we_can_convert_comparison_binary_expr_to_proof_expr_with_scale_cast() {
310        let schema = vec![
311            ("column1".into(), ColumnType::SmallInt),
312            (
313                "column2".into(),
314                ColumnType::Decimal75(Precision::new(25).unwrap(), 5),
315            ),
316            (
317                "column3".into(),
318                ColumnType::Decimal75(Precision::new(75).unwrap(), 5),
319            ),
320        ];
321
322        // Eq
323        let expr = df_column("namespace.table_name", "column1")
324            .eq(df_column("namespace.table_name", "column3"));
325        assert_eq!(
326            expr_to_proof_expr(&expr, &schema).unwrap(),
327            DynProofExpr::try_new_equals(
328                DynProofExpr::try_new_scaling_cast(
329                    COLUMN1_SMALLINT(),
330                    ColumnType::Decimal75(
331                        Precision::new(10).expect("Precision is definitely valid"),
332                        5
333                    )
334                )
335                .unwrap(),
336                COLUMN3_DECIMAL_75_5()
337            )
338            .unwrap()
339        );
340
341        // Lt
342        let expr = df_column("namespace.table_name", "column1")
343            .lt(df_column("namespace.table_name", "column2"));
344        assert_eq!(
345            expr_to_proof_expr(&expr, &schema).unwrap(),
346            DynProofExpr::try_new_inequality(
347                DynProofExpr::try_new_scaling_cast(
348                    COLUMN1_SMALLINT(),
349                    ColumnType::Decimal75(
350                        Precision::new(10).expect("Precision is definitely valid"),
351                        5
352                    )
353                )
354                .unwrap(),
355                COLUMN2_DECIMAL_25_5(),
356                true
357            )
358            .unwrap()
359        );
360
361        // Gt
362        let expr = df_column("namespace.table_name", "column1")
363            .gt(df_column("namespace.table_name", "column2"));
364        assert_eq!(
365            expr_to_proof_expr(&expr, &schema).unwrap(),
366            DynProofExpr::try_new_inequality(
367                DynProofExpr::try_new_scaling_cast(
368                    COLUMN1_SMALLINT(),
369                    ColumnType::Decimal75(
370                        Precision::new(10).expect("Precision is definitely valid"),
371                        5
372                    )
373                )
374                .unwrap(),
375                COLUMN2_DECIMAL_25_5(),
376                false
377            )
378            .unwrap()
379        );
380
381        // LtEq
382        let expr = df_column("namespace.table_name", "column1")
383            .lt_eq(df_column("namespace.table_name", "column2"));
384        assert_eq!(
385            expr_to_proof_expr(&expr, &schema).unwrap(),
386            DynProofExpr::try_new_not(
387                DynProofExpr::try_new_inequality(
388                    DynProofExpr::try_new_scaling_cast(
389                        COLUMN1_SMALLINT(),
390                        ColumnType::Decimal75(
391                            Precision::new(10).expect("Precision is definitely valid"),
392                            5
393                        )
394                    )
395                    .unwrap(),
396                    COLUMN2_DECIMAL_25_5(),
397                    false
398                )
399                .unwrap()
400            )
401            .unwrap()
402        );
403
404        // GtEq
405        let expr = df_column("namespace.table_name", "column1")
406            .gt_eq(df_column("namespace.table_name", "column2"));
407        assert_eq!(
408            expr_to_proof_expr(&expr, &schema).unwrap(),
409            DynProofExpr::try_new_not(
410                DynProofExpr::try_new_inequality(
411                    DynProofExpr::try_new_scaling_cast(
412                        COLUMN1_SMALLINT(),
413                        ColumnType::Decimal75(
414                            Precision::new(10).expect("Precision is definitely valid"),
415                            5
416                        )
417                    )
418                    .unwrap(),
419                    COLUMN2_DECIMAL_25_5(),
420                    true
421                )
422                .unwrap()
423            )
424            .unwrap()
425        );
426    }
427
428    #[test]
429    fn we_can_convert_arithmetic_binary_expr_to_proof_expr() {
430        let schema = vec![
431            ("column1".into(), ColumnType::SmallInt),
432            ("column2".into(), ColumnType::BigInt),
433        ];
434
435        // Plus
436        let expr = Expr::BinaryExpr(BinaryExpr {
437            left: Box::new(df_column("namespace.table_name", "column1")),
438            right: Box::new(df_column("namespace.table_name", "column2")),
439            op: Operator::Plus,
440        });
441        assert_eq!(
442            expr_to_proof_expr(&expr, &schema).unwrap(),
443            DynProofExpr::try_new_add(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
444        );
445
446        // Minus
447        let expr = Expr::BinaryExpr(BinaryExpr {
448            left: Box::new(df_column("namespace.table_name", "column1")),
449            right: Box::new(df_column("namespace.table_name", "column2")),
450            op: Operator::Minus,
451        });
452        assert_eq!(
453            expr_to_proof_expr(&expr, &schema).unwrap(),
454            DynProofExpr::try_new_subtract(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
455        );
456
457        // Multiply
458        let expr = Expr::BinaryExpr(BinaryExpr {
459            left: Box::new(df_column("namespace.table_name", "column1")),
460            right: Box::new(df_column("namespace.table_name", "column2")),
461            op: Operator::Multiply,
462        });
463        assert_eq!(
464            expr_to_proof_expr(&expr, &schema).unwrap(),
465            DynProofExpr::try_new_multiply(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
466        );
467    }
468
469    #[test]
470    fn we_can_convert_arithmetic_binary_expr_to_proof_expr_with_scale_cast() {
471        let schema = vec![
472            ("column1".into(), ColumnType::SmallInt),
473            (
474                "column2".into(),
475                ColumnType::Decimal75(Precision::new(25).unwrap(), 5),
476            ),
477            (
478                "column3".into(),
479                ColumnType::Decimal75(Precision::new(75).unwrap(), 5),
480            ),
481        ];
482
483        // Add
484        let expr = df_column("namespace.table_name", "column1")
485            .add(df_column("namespace.table_name", "column2"));
486        assert_eq!(
487            expr_to_proof_expr(&expr, &schema).unwrap(),
488            DynProofExpr::try_new_add(
489                DynProofExpr::try_new_scaling_cast(
490                    COLUMN1_SMALLINT(),
491                    ColumnType::Decimal75(
492                        Precision::new(10).expect("Precision is definitely valid"),
493                        5
494                    )
495                )
496                .unwrap(),
497                COLUMN2_DECIMAL_25_5()
498            )
499            .unwrap()
500        );
501
502        // Subtract
503        let expr = df_column("namespace.table_name", "column1")
504            .sub(df_column("namespace.table_name", "column2"));
505        assert_eq!(
506            expr_to_proof_expr(&expr, &schema).unwrap(),
507            DynProofExpr::try_new_subtract(
508                DynProofExpr::try_new_scaling_cast(
509                    COLUMN1_SMALLINT(),
510                    ColumnType::Decimal75(
511                        Precision::new(10).expect("Precision is definitely valid"),
512                        5
513                    )
514                )
515                .unwrap(),
516                COLUMN2_DECIMAL_25_5()
517            )
518            .unwrap()
519        );
520
521        // Multiply - No scale cast!
522        let expr = df_column("namespace.table_name", "column1")
523            .mul(df_column("namespace.table_name", "column2"));
524        assert_eq!(
525            expr_to_proof_expr(&expr, &schema).unwrap(),
526            DynProofExpr::try_new_multiply(COLUMN1_SMALLINT(), COLUMN2_DECIMAL_25_5()).unwrap()
527        );
528    }
529
530    #[test]
531    fn we_can_convert_logical_binary_expr_to_proof_expr() {
532        let schema = vec![
533            ("column1".into(), ColumnType::Boolean),
534            ("column2".into(), ColumnType::Boolean),
535        ];
536
537        // And
538        let expr = df_column("namespace.table_name", "column1")
539            .and(df_column("namespace.table_name", "column2"));
540        assert_eq!(
541            expr_to_proof_expr(&expr, &schema).unwrap(),
542            DynProofExpr::try_new_and(COLUMN1_BOOLEAN(), COLUMN2_BOOLEAN()).unwrap()
543        );
544
545        // Or
546        let expr = df_column("namespace.table_name", "column1")
547            .or(df_column("namespace.table_name", "column2"));
548        assert_eq!(
549            expr_to_proof_expr(&expr, &schema).unwrap(),
550            DynProofExpr::try_new_or(COLUMN1_BOOLEAN(), COLUMN2_BOOLEAN()).unwrap()
551        );
552    }
553
554    #[test]
555    fn we_can_convert_logical_not_eq_to_proof_expr() {
556        let schema = vec![
557            ("column1".into(), ColumnType::BigInt),
558            ("column2".into(), ColumnType::BigInt),
559        ];
560
561        let expr = df_column("namespace.table_name", "column1")
562            .not_eq(df_column("namespace.table_name", "column2"));
563        assert_eq!(
564            expr_to_proof_expr(&expr, &schema).unwrap(),
565            DynProofExpr::try_new_not(
566                DynProofExpr::try_new_equals(
567                    DynProofExpr::new_column(ColumnRef::new(
568                        TableRef::from_names(Some("namespace"), "table_name"),
569                        "column1".into(),
570                        ColumnType::BigInt,
571                    )),
572                    DynProofExpr::new_column(ColumnRef::new(
573                        TableRef::from_names(Some("namespace"), "table_name"),
574                        "column2".into(),
575                        ColumnType::BigInt,
576                    ))
577                )
578                .unwrap()
579            )
580            .unwrap()
581        );
582    }
583
584    #[test]
585    fn we_cannot_convert_unsupported_binary_expr_to_proof_expr() {
586        // Unsupported binary operator
587        let expr = Expr::BinaryExpr(BinaryExpr {
588            left: Box::new(df_column("namespace.table_name", "column1")),
589            right: Box::new(df_column("namespace.table_name", "column2")),
590            op: Operator::AtArrow,
591        });
592        let schema = vec![
593            ("column1".into(), ColumnType::Boolean),
594            ("column2".into(), ColumnType::Boolean),
595        ];
596        assert!(matches!(
597            expr_to_proof_expr(&expr, &schema),
598            Err(PlannerError::UnsupportedBinaryOperator { .. })
599        ));
600    }
601
602    // Literal
603    #[test]
604    fn we_can_convert_literal_expr_to_proof_expr() {
605        let expr = Expr::Literal(ScalarValue::Int32(Some(1)));
606        assert_eq!(
607            expr_to_proof_expr(&expr, &Vec::new()).unwrap(),
608            DynProofExpr::new_literal(LiteralValue::Int(1))
609        );
610    }
611
612    // Not
613    #[test]
614    fn we_can_convert_not_expr_to_proof_expr() {
615        let expr = Expr::Not(Box::new(df_column("table_name", "column")));
616        let schema = vec![("column".into(), ColumnType::Boolean)];
617        assert_eq!(
618            expr_to_proof_expr(&expr, &schema).unwrap(),
619            DynProofExpr::try_new_not(DynProofExpr::new_column(ColumnRef::new(
620                TableRef::from_names(None, "table_name"),
621                "column".into(),
622                ColumnType::Boolean
623            )))
624            .unwrap()
625        );
626    }
627
628    // Cast
629    #[test]
630    fn we_can_convert_cast_expr_to_proof_expr() {
631        let expr = Expr::Cast(Cast::new(
632            Box::new(Expr::Literal(ScalarValue::Boolean(Some(true)))),
633            DataType::Int32,
634        ));
635        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
636        assert_eq!(
637            expression,
638            DynProofExpr::try_new_cast(
639                DynProofExpr::new_literal(LiteralValue::Boolean(true)),
640                ColumnType::Int
641            )
642            .unwrap()
643        );
644    }
645
646    #[test]
647    fn we_cannot_convert_cast_expr_to_proof_expr_when_inner_expr_to_proof_expr_fails() {
648        // Unsupported logical expression
649        let expr = Expr::Cast(Cast::new(
650            Box::new(Expr::Literal(ScalarValue::UInt64(Some(100)))),
651            DataType::Int16,
652        ));
653        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
654        assert!(matches!(
655            expression,
656            PlannerError::UnsupportedDataType { data_type: _ }
657        ));
658    }
659
660    #[test]
661    fn we_cannot_convert_cast_expr_to_proof_expr_for_unsupported_datatypes() {
662        // Unsupported logical expression
663        let expr = Expr::Cast(Cast::new(
664            Box::new(Expr::Literal(ScalarValue::Boolean(Some(true)))),
665            DataType::UInt16,
666        ));
667        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
668        assert!(matches!(
669            expression,
670            PlannerError::UnsupportedDataType { data_type: _ }
671        ));
672    }
673
674    #[test]
675    fn we_cannot_convert_cast_expr_to_proof_expr_for_datatypes_for_which_casting_is_not_supported()
676    {
677        // Unsupported logical expression
678        let expr = Expr::Cast(Cast::new(
679            Box::new(Expr::Literal(ScalarValue::Int16(Some(100)))),
680            DataType::Boolean,
681        ));
682        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
683        assert!(matches!(
684            expression,
685            PlannerError::AnalyzeError { source: _ }
686        ));
687    }
688
689    // Placeholder
690    #[test]
691    fn we_can_convert_placeholder_to_proof_expr() {
692        let expr = Expr::Placeholder(Placeholder {
693            id: "$1".to_string(),
694            data_type: Some(DataType::Int32),
695        });
696        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
697        assert_eq!(
698            expression,
699            DynProofExpr::try_new_placeholder(1, ColumnType::Int).unwrap()
700        );
701    }
702
703    // Placeholder with data type specified by cast
704    #[test]
705    fn we_can_convert_placeholder_with_data_type_specified_by_cast_to_proof_expr() {
706        let expr = Expr::Cast(Cast::new(
707            Box::new(Expr::Placeholder(Placeholder {
708                id: "$1".to_string(),
709                data_type: None,
710            })),
711            DataType::Int32,
712        ));
713        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
714        assert_eq!(
715            expression,
716            DynProofExpr::try_new_placeholder(1, ColumnType::Int).unwrap()
717        );
718    }
719
720    // Unsupported logical expression
721    #[test]
722    fn we_cannot_convert_unsupported_expr_to_proof_expr() {
723        let expr = Expr::OuterReferenceColumn(
724            DataType::Int32,
725            Column::new(None::<TableReference>, "column"),
726        );
727        assert!(matches!(
728            expr_to_proof_expr(&expr, &Vec::new()),
729            Err(PlannerError::UnsupportedLogicalExpression { .. })
730        ));
731    }
732
733    #[test]
734    fn we_can_get_proof_expr_for_timestamps_of_different_scale() {
735        let lhs = Expr::Literal(ScalarValue::TimestampSecond(Some(1), None));
736        let rhs = Expr::Literal(ScalarValue::TimestampNanosecond(Some(1), None));
737        binary_expr_to_proof_expr(&lhs, &rhs, Operator::Gt, &Vec::new()).unwrap();
738    }
739}