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        common::ScalarValue,
155        logical_expr::{
156            expr::{Placeholder, Unnest},
157            Cast,
158        },
159    };
160    use proof_of_sql::base::{
161        database::{ColumnRef, ColumnType, LiteralValue, TableRef},
162        math::decimal::Precision,
163    };
164
165    #[expect(non_snake_case)]
166    fn COLUMN_INT() -> DynProofExpr {
167        DynProofExpr::new_column(ColumnRef::new(
168            TableRef::from_names(Some("namespace"), "table_name"),
169            "column".into(),
170            ColumnType::Int,
171        ))
172    }
173
174    #[expect(non_snake_case)]
175    fn COLUMN1_SMALLINT() -> DynProofExpr {
176        DynProofExpr::new_column(ColumnRef::new(
177            TableRef::from_names(Some("namespace"), "table_name"),
178            "column1".into(),
179            ColumnType::SmallInt,
180        ))
181    }
182
183    #[expect(non_snake_case)]
184    fn COLUMN2_BIGINT() -> DynProofExpr {
185        DynProofExpr::new_column(ColumnRef::new(
186            TableRef::from_names(Some("namespace"), "table_name"),
187            "column2".into(),
188            ColumnType::BigInt,
189        ))
190    }
191
192    #[expect(non_snake_case)]
193    fn COLUMN1_BOOLEAN() -> DynProofExpr {
194        DynProofExpr::new_column(ColumnRef::new(
195            TableRef::from_names(Some("namespace"), "table_name"),
196            "column1".into(),
197            ColumnType::Boolean,
198        ))
199    }
200
201    #[expect(non_snake_case)]
202    fn COLUMN2_BOOLEAN() -> DynProofExpr {
203        DynProofExpr::new_column(ColumnRef::new(
204            TableRef::from_names(Some("namespace"), "table_name"),
205            "column2".into(),
206            ColumnType::Boolean,
207        ))
208    }
209
210    #[expect(non_snake_case)]
211    fn COLUMN3_DECIMAL_75_5() -> DynProofExpr {
212        DynProofExpr::new_column(ColumnRef::new(
213            TableRef::from_names(Some("namespace"), "table_name"),
214            "column3".into(),
215            ColumnType::Decimal75(
216                Precision::new(75).expect("Precision is definitely valid"),
217                5,
218            ),
219        ))
220    }
221
222    #[expect(non_snake_case)]
223    fn COLUMN2_DECIMAL_25_5() -> DynProofExpr {
224        DynProofExpr::new_column(ColumnRef::new(
225            TableRef::from_names(Some("namespace"), "table_name"),
226            "column2".into(),
227            ColumnType::Decimal75(
228                Precision::new(25).expect("Precision is definitely valid"),
229                5,
230            ),
231        ))
232    }
233
234    // Alias
235    #[test]
236    fn we_can_convert_alias_to_proof_expr() {
237        // Column
238        let expr = df_column("namespace.table_name", "column").alias("alias");
239        let schema = vec![("column".into(), ColumnType::Int)];
240        assert_eq!(expr_to_proof_expr(&expr, &schema).unwrap(), COLUMN_INT());
241    }
242
243    // Column
244    #[test]
245    fn we_can_convert_column_expr_to_proof_expr() {
246        // Column
247        let expr = df_column("namespace.table_name", "column");
248        let schema = vec![("column".into(), ColumnType::Int)];
249        assert_eq!(expr_to_proof_expr(&expr, &schema).unwrap(), COLUMN_INT());
250    }
251
252    // BinaryExpr
253    #[test]
254    fn we_can_convert_comparison_binary_expr_to_proof_expr() {
255        let schema = vec![
256            ("column1".into(), ColumnType::SmallInt),
257            ("column2".into(), ColumnType::BigInt),
258        ];
259
260        // Eq
261        let expr = df_column("namespace.table_name", "column1")
262            .eq(df_column("namespace.table_name", "column2"));
263        assert_eq!(
264            expr_to_proof_expr(&expr, &schema).unwrap(),
265            DynProofExpr::try_new_equals(COLUMN1_SMALLINT(), COLUMN2_BIGINT()).unwrap()
266        );
267
268        // Lt
269        let expr = df_column("namespace.table_name", "column1")
270            .lt(df_column("namespace.table_name", "column2"));
271        assert_eq!(
272            expr_to_proof_expr(&expr, &schema).unwrap(),
273            DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), true).unwrap()
274        );
275
276        // Gt
277        let expr = df_column("namespace.table_name", "column1")
278            .gt(df_column("namespace.table_name", "column2"));
279        assert_eq!(
280            expr_to_proof_expr(&expr, &schema).unwrap(),
281            DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), false).unwrap()
282        );
283
284        // LtEq
285        let expr = df_column("namespace.table_name", "column1")
286            .lt_eq(df_column("namespace.table_name", "column2"));
287        assert_eq!(
288            expr_to_proof_expr(&expr, &schema).unwrap(),
289            DynProofExpr::try_new_not(
290                DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), false)
291                    .unwrap()
292            )
293            .unwrap()
294        );
295
296        // GtEq
297        let expr = df_column("namespace.table_name", "column1")
298            .gt_eq(df_column("namespace.table_name", "column2"));
299        assert_eq!(
300            expr_to_proof_expr(&expr, &schema).unwrap(),
301            DynProofExpr::try_new_not(
302                DynProofExpr::try_new_inequality(COLUMN1_SMALLINT(), COLUMN2_BIGINT(), true)
303                    .unwrap()
304            )
305            .unwrap()
306        );
307    }
308
309    #[expect(clippy::too_many_lines)]
310    #[test]
311    fn we_can_convert_comparison_binary_expr_to_proof_expr_with_scale_cast() {
312        let schema = vec![
313            ("column1".into(), ColumnType::SmallInt),
314            (
315                "column2".into(),
316                ColumnType::Decimal75(Precision::new(25).unwrap(), 5),
317            ),
318            (
319                "column3".into(),
320                ColumnType::Decimal75(Precision::new(75).unwrap(), 5),
321            ),
322        ];
323
324        // Eq
325        let expr = df_column("namespace.table_name", "column1")
326            .eq(df_column("namespace.table_name", "column3"));
327        assert_eq!(
328            expr_to_proof_expr(&expr, &schema).unwrap(),
329            DynProofExpr::try_new_equals(
330                DynProofExpr::try_new_scaling_cast(
331                    COLUMN1_SMALLINT(),
332                    ColumnType::Decimal75(
333                        Precision::new(10).expect("Precision is definitely valid"),
334                        5
335                    )
336                )
337                .unwrap(),
338                COLUMN3_DECIMAL_75_5()
339            )
340            .unwrap()
341        );
342
343        // Lt
344        let expr = df_column("namespace.table_name", "column1")
345            .lt(df_column("namespace.table_name", "column2"));
346        assert_eq!(
347            expr_to_proof_expr(&expr, &schema).unwrap(),
348            DynProofExpr::try_new_inequality(
349                DynProofExpr::try_new_scaling_cast(
350                    COLUMN1_SMALLINT(),
351                    ColumnType::Decimal75(
352                        Precision::new(10).expect("Precision is definitely valid"),
353                        5
354                    )
355                )
356                .unwrap(),
357                COLUMN2_DECIMAL_25_5(),
358                true
359            )
360            .unwrap()
361        );
362
363        // Gt
364        let expr = df_column("namespace.table_name", "column1")
365            .gt(df_column("namespace.table_name", "column2"));
366        assert_eq!(
367            expr_to_proof_expr(&expr, &schema).unwrap(),
368            DynProofExpr::try_new_inequality(
369                DynProofExpr::try_new_scaling_cast(
370                    COLUMN1_SMALLINT(),
371                    ColumnType::Decimal75(
372                        Precision::new(10).expect("Precision is definitely valid"),
373                        5
374                    )
375                )
376                .unwrap(),
377                COLUMN2_DECIMAL_25_5(),
378                false
379            )
380            .unwrap()
381        );
382
383        // LtEq
384        let expr = df_column("namespace.table_name", "column1")
385            .lt_eq(df_column("namespace.table_name", "column2"));
386        assert_eq!(
387            expr_to_proof_expr(&expr, &schema).unwrap(),
388            DynProofExpr::try_new_not(
389                DynProofExpr::try_new_inequality(
390                    DynProofExpr::try_new_scaling_cast(
391                        COLUMN1_SMALLINT(),
392                        ColumnType::Decimal75(
393                            Precision::new(10).expect("Precision is definitely valid"),
394                            5
395                        )
396                    )
397                    .unwrap(),
398                    COLUMN2_DECIMAL_25_5(),
399                    false
400                )
401                .unwrap()
402            )
403            .unwrap()
404        );
405
406        // GtEq
407        let expr = df_column("namespace.table_name", "column1")
408            .gt_eq(df_column("namespace.table_name", "column2"));
409        assert_eq!(
410            expr_to_proof_expr(&expr, &schema).unwrap(),
411            DynProofExpr::try_new_not(
412                DynProofExpr::try_new_inequality(
413                    DynProofExpr::try_new_scaling_cast(
414                        COLUMN1_SMALLINT(),
415                        ColumnType::Decimal75(
416                            Precision::new(10).expect("Precision is definitely valid"),
417                            5
418                        )
419                    )
420                    .unwrap(),
421                    COLUMN2_DECIMAL_25_5(),
422                    true
423                )
424                .unwrap()
425            )
426            .unwrap()
427        );
428    }
429
430    #[test]
431    fn we_can_convert_arithmetic_binary_expr_to_proof_expr() {
432        let schema = vec![
433            ("column1".into(), ColumnType::SmallInt),
434            ("column2".into(), ColumnType::BigInt),
435        ];
436
437        // Plus
438        let expr = Expr::BinaryExpr(BinaryExpr {
439            left: Box::new(df_column("namespace.table_name", "column1")),
440            right: Box::new(df_column("namespace.table_name", "column2")),
441            op: Operator::Plus,
442        });
443        assert_eq!(
444            expr_to_proof_expr(&expr, &schema).unwrap(),
445            DynProofExpr::try_new_add(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
446        );
447
448        // Minus
449        let expr = Expr::BinaryExpr(BinaryExpr {
450            left: Box::new(df_column("namespace.table_name", "column1")),
451            right: Box::new(df_column("namespace.table_name", "column2")),
452            op: Operator::Minus,
453        });
454        assert_eq!(
455            expr_to_proof_expr(&expr, &schema).unwrap(),
456            DynProofExpr::try_new_subtract(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
457        );
458
459        // Multiply
460        let expr = Expr::BinaryExpr(BinaryExpr {
461            left: Box::new(df_column("namespace.table_name", "column1")),
462            right: Box::new(df_column("namespace.table_name", "column2")),
463            op: Operator::Multiply,
464        });
465        assert_eq!(
466            expr_to_proof_expr(&expr, &schema).unwrap(),
467            DynProofExpr::try_new_multiply(COLUMN1_SMALLINT(), COLUMN2_BIGINT(),).unwrap()
468        );
469    }
470
471    #[test]
472    fn we_can_convert_arithmetic_binary_expr_to_proof_expr_with_scale_cast() {
473        let schema = vec![
474            ("column1".into(), ColumnType::SmallInt),
475            (
476                "column2".into(),
477                ColumnType::Decimal75(Precision::new(25).unwrap(), 5),
478            ),
479            (
480                "column3".into(),
481                ColumnType::Decimal75(Precision::new(75).unwrap(), 5),
482            ),
483        ];
484
485        // Add
486        let expr = df_column("namespace.table_name", "column1")
487            .add(df_column("namespace.table_name", "column2"));
488        assert_eq!(
489            expr_to_proof_expr(&expr, &schema).unwrap(),
490            DynProofExpr::try_new_add(
491                DynProofExpr::try_new_scaling_cast(
492                    COLUMN1_SMALLINT(),
493                    ColumnType::Decimal75(
494                        Precision::new(10).expect("Precision is definitely valid"),
495                        5
496                    )
497                )
498                .unwrap(),
499                COLUMN2_DECIMAL_25_5()
500            )
501            .unwrap()
502        );
503
504        // Subtract
505        let expr = df_column("namespace.table_name", "column1")
506            .sub(df_column("namespace.table_name", "column2"));
507        assert_eq!(
508            expr_to_proof_expr(&expr, &schema).unwrap(),
509            DynProofExpr::try_new_subtract(
510                DynProofExpr::try_new_scaling_cast(
511                    COLUMN1_SMALLINT(),
512                    ColumnType::Decimal75(
513                        Precision::new(10).expect("Precision is definitely valid"),
514                        5
515                    )
516                )
517                .unwrap(),
518                COLUMN2_DECIMAL_25_5()
519            )
520            .unwrap()
521        );
522
523        // Multiply - No scale cast!
524        let expr = df_column("namespace.table_name", "column1")
525            .mul(df_column("namespace.table_name", "column2"));
526        assert_eq!(
527            expr_to_proof_expr(&expr, &schema).unwrap(),
528            DynProofExpr::try_new_multiply(COLUMN1_SMALLINT(), COLUMN2_DECIMAL_25_5()).unwrap()
529        );
530    }
531
532    #[test]
533    fn we_can_convert_logical_binary_expr_to_proof_expr() {
534        let schema = vec![
535            ("column1".into(), ColumnType::Boolean),
536            ("column2".into(), ColumnType::Boolean),
537        ];
538
539        // And
540        let expr = df_column("namespace.table_name", "column1")
541            .and(df_column("namespace.table_name", "column2"));
542        assert_eq!(
543            expr_to_proof_expr(&expr, &schema).unwrap(),
544            DynProofExpr::try_new_and(COLUMN1_BOOLEAN(), COLUMN2_BOOLEAN()).unwrap()
545        );
546
547        // Or
548        let expr = df_column("namespace.table_name", "column1")
549            .or(df_column("namespace.table_name", "column2"));
550        assert_eq!(
551            expr_to_proof_expr(&expr, &schema).unwrap(),
552            DynProofExpr::try_new_or(COLUMN1_BOOLEAN(), COLUMN2_BOOLEAN()).unwrap()
553        );
554    }
555
556    #[test]
557    fn we_can_convert_logical_not_eq_to_proof_expr() {
558        let schema = vec![
559            ("column1".into(), ColumnType::BigInt),
560            ("column2".into(), ColumnType::BigInt),
561        ];
562
563        let expr = df_column("namespace.table_name", "column1")
564            .not_eq(df_column("namespace.table_name", "column2"));
565        assert_eq!(
566            expr_to_proof_expr(&expr, &schema).unwrap(),
567            DynProofExpr::try_new_not(
568                DynProofExpr::try_new_equals(
569                    DynProofExpr::new_column(ColumnRef::new(
570                        TableRef::from_names(Some("namespace"), "table_name"),
571                        "column1".into(),
572                        ColumnType::BigInt,
573                    )),
574                    DynProofExpr::new_column(ColumnRef::new(
575                        TableRef::from_names(Some("namespace"), "table_name"),
576                        "column2".into(),
577                        ColumnType::BigInt,
578                    ))
579                )
580                .unwrap()
581            )
582            .unwrap()
583        );
584    }
585
586    #[test]
587    fn we_cannot_convert_unsupported_binary_expr_to_proof_expr() {
588        // Unsupported binary operator
589        let expr = Expr::BinaryExpr(BinaryExpr {
590            left: Box::new(df_column("namespace.table_name", "column1")),
591            right: Box::new(df_column("namespace.table_name", "column2")),
592            op: Operator::AtArrow,
593        });
594        let schema = vec![
595            ("column1".into(), ColumnType::Boolean),
596            ("column2".into(), ColumnType::Boolean),
597        ];
598        assert!(matches!(
599            expr_to_proof_expr(&expr, &schema),
600            Err(PlannerError::UnsupportedBinaryOperator { .. })
601        ));
602    }
603
604    // Literal
605    #[test]
606    fn we_can_convert_literal_expr_to_proof_expr() {
607        let expr = Expr::Literal(ScalarValue::Int32(Some(1)));
608        assert_eq!(
609            expr_to_proof_expr(&expr, &Vec::new()).unwrap(),
610            DynProofExpr::new_literal(LiteralValue::Int(1))
611        );
612    }
613
614    // Not
615    #[test]
616    fn we_can_convert_not_expr_to_proof_expr() {
617        let expr = Expr::Not(Box::new(df_column("table_name", "column")));
618        let schema = vec![("column".into(), ColumnType::Boolean)];
619        assert_eq!(
620            expr_to_proof_expr(&expr, &schema).unwrap(),
621            DynProofExpr::try_new_not(DynProofExpr::new_column(ColumnRef::new(
622                TableRef::from_names(None, "table_name"),
623                "column".into(),
624                ColumnType::Boolean
625            )))
626            .unwrap()
627        );
628    }
629
630    // Cast
631    #[test]
632    fn we_can_convert_cast_expr_to_proof_expr() {
633        let expr = Expr::Cast(Cast::new(
634            Box::new(Expr::Literal(ScalarValue::Boolean(Some(true)))),
635            DataType::Int32,
636        ));
637        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
638        assert_eq!(
639            expression,
640            DynProofExpr::try_new_cast(
641                DynProofExpr::new_literal(LiteralValue::Boolean(true)),
642                ColumnType::Int
643            )
644            .unwrap()
645        );
646    }
647
648    #[test]
649    fn we_cannot_convert_cast_expr_to_proof_expr_when_inner_expr_to_proof_expr_fails() {
650        // Unsupported logical expression
651        let expr = Expr::Cast(Cast::new(
652            Box::new(Expr::Literal(ScalarValue::UInt64(Some(100)))),
653            DataType::Int16,
654        ));
655        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
656        assert!(matches!(
657            expression,
658            PlannerError::UnsupportedDataType { data_type: _ }
659        ));
660    }
661
662    #[test]
663    fn we_cannot_convert_cast_expr_to_proof_expr_for_unsupported_datatypes() {
664        // Unsupported logical expression
665        let expr = Expr::Cast(Cast::new(
666            Box::new(Expr::Literal(ScalarValue::Boolean(Some(true)))),
667            DataType::UInt16,
668        ));
669        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
670        assert!(matches!(
671            expression,
672            PlannerError::UnsupportedDataType { data_type: _ }
673        ));
674    }
675
676    #[test]
677    fn we_cannot_convert_cast_expr_to_proof_expr_for_datatypes_for_which_casting_is_not_supported()
678    {
679        // Unsupported logical expression
680        let expr = Expr::Cast(Cast::new(
681            Box::new(Expr::Literal(ScalarValue::Int16(Some(100)))),
682            DataType::Boolean,
683        ));
684        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap_err();
685        assert!(matches!(
686            expression,
687            PlannerError::AnalyzeError { source: _ }
688        ));
689    }
690
691    // Placeholder
692    #[test]
693    fn we_can_convert_placeholder_to_proof_expr() {
694        let expr = Expr::Placeholder(Placeholder {
695            id: "$1".to_string(),
696            data_type: Some(DataType::Int32),
697        });
698        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
699        assert_eq!(
700            expression,
701            DynProofExpr::try_new_placeholder(1, ColumnType::Int).unwrap()
702        );
703    }
704
705    // Placeholder with data type specified by cast
706    #[test]
707    fn we_can_convert_placeholder_with_data_type_specified_by_cast_to_proof_expr() {
708        let expr = Expr::Cast(Cast::new(
709            Box::new(Expr::Placeholder(Placeholder {
710                id: "$1".to_string(),
711                data_type: None,
712            })),
713            DataType::Int32,
714        ));
715        let expression = expr_to_proof_expr(&expr, &Vec::new()).unwrap();
716        assert_eq!(
717            expression,
718            DynProofExpr::try_new_placeholder(1, ColumnType::Int).unwrap()
719        );
720    }
721
722    // Unsupported logical expression
723    #[test]
724    fn we_cannot_convert_unsupported_expr_to_proof_expr() {
725        let expr = Expr::Unnest(Unnest::new(Expr::Literal(ScalarValue::Int32(Some(100)))));
726        assert!(matches!(
727            expr_to_proof_expr(&expr, &Vec::new()),
728            Err(PlannerError::UnsupportedLogicalExpression { .. })
729        ));
730    }
731
732    #[test]
733    fn we_can_get_proof_expr_for_timestamps_of_different_scale() {
734        let lhs = Expr::Literal(ScalarValue::TimestampSecond(Some(1), None));
735        let rhs = Expr::Literal(ScalarValue::TimestampNanosecond(Some(1), None));
736        binary_expr_to_proof_expr(&lhs, &rhs, Operator::Gt, &Vec::new()).unwrap();
737    }
738}