otter_sql/
codegen.rs

1//! Intermediate code generation from the AST.
2use sqlparser::{
3    ast::{self, SelectItem, SetExpr, Statement, TableFactor, TableWithJoins},
4    parser::ParserError,
5};
6
7use std::{error::Error, fmt::Display};
8
9use crate::{
10    expr::{Expr, ExprError},
11    ic::{Instruction, IntermediateCode},
12    identifier::IdentifierError,
13    parser::parse,
14    value::{Value, ValueError},
15    vm::RegisterIndex,
16};
17
18/// Represents either a parser error or a codegen error.
19#[derive(Debug)]
20pub enum ParserOrCodegenError {
21    ParserError(ParserError),
22    CodegenError(CodegenError),
23}
24
25impl Display for ParserOrCodegenError {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            Self::ParserError(e) => write!(f, "{}", e),
29            Self::CodegenError(e) => write!(f, "{}", e),
30        }
31    }
32}
33
34impl Error for ParserOrCodegenError {}
35
36impl From<ParserError> for ParserOrCodegenError {
37    fn from(e: ParserError) -> Self {
38        Self::ParserError(e)
39    }
40}
41
42impl From<CodegenError> for ParserOrCodegenError {
43    fn from(e: CodegenError) -> Self {
44        Self::CodegenError(e)
45    }
46}
47
48/// Generates intermediate code for the given SQL.
49pub fn codegen_str(code: &str) -> Result<Vec<IntermediateCode>, ParserOrCodegenError> {
50    let ast = parse(code)?;
51    Ok(ast
52        .into_iter()
53        .map(|stmt| codegen_ast(&stmt))
54        .collect::<Result<Vec<_>, CodegenError>>()?)
55}
56
57/// Generates intermediate code from the AST.
58pub fn codegen_ast(ast: &Statement) -> Result<IntermediateCode, CodegenError> {
59    let mut instrs = Vec::<Instruction>::new();
60
61    let mut current_reg = RegisterIndex::default();
62
63    match ast {
64        Statement::CreateTable {
65            // TODO: support `OR REPLACE`
66            or_replace: _,
67            // TODO: support temp tables
68            temporary: _,
69            external: _,
70            global: _,
71            if_not_exists,
72            name,
73            columns,
74            // TODO: support table level constraints
75            constraints: _,
76            hive_distribution: _,
77            hive_formats: _,
78            table_properties: _,
79            with_options: _,
80            file_format: _,
81            location: _,
82            query: _,
83            without_rowid: _,
84            like: _,
85            engine: _,
86            default_charset: _,
87            collation: _,
88            on_commit: _,
89        } => {
90            let table_reg_index = current_reg;
91            instrs.push(Instruction::Empty {
92                index: table_reg_index,
93            });
94            current_reg = current_reg.next_index();
95
96            let col_reg_index = current_reg;
97            current_reg = current_reg.next_index();
98            for col in columns {
99                instrs.push(Instruction::ColumnDef {
100                    index: col_reg_index,
101                    name: col.name.value.as_str().into(),
102                    data_type: col.data_type.clone(),
103                });
104
105                for option in col.options.iter() {
106                    instrs.push(Instruction::AddColumnOption {
107                        index: col_reg_index,
108                        option: option.clone(),
109                    });
110                }
111
112                instrs.push(Instruction::AddColumn {
113                    table_reg_index,
114                    col_index: col_reg_index,
115                });
116            }
117
118            instrs.push(Instruction::NewTable {
119                index: table_reg_index,
120                name: name.0.clone().try_into()?,
121                exists_ok: *if_not_exists,
122            });
123            Ok(())
124        }
125        Statement::Insert {
126            or: _,
127            into: _,
128            table_name,
129            columns,
130            overwrite: _,
131            source,
132            partitioned: _,
133            after_columns: _,
134            table: _,
135            on: _,
136        } => {
137            let table_reg_index = current_reg;
138            instrs.push(Instruction::Source {
139                index: table_reg_index,
140                name: table_name.0.clone().try_into()?,
141            });
142            current_reg = current_reg.next_index();
143
144            let insert_reg_index = current_reg;
145            instrs.push(Instruction::InsertDef {
146                table_reg_index,
147                index: insert_reg_index,
148            });
149            current_reg = current_reg.next_index();
150
151            for col in columns {
152                instrs.push(Instruction::ColumnInsertDef {
153                    insert_index: insert_reg_index,
154                    col_name: col.value.as_str().into(),
155                })
156            }
157
158            // only values source for now
159            match source.as_ref() {
160                ast::Query {
161                    body: ast::SetExpr::Values(values),
162                    ..
163                } => {
164                    for row in values.0.clone() {
165                        let row_reg = current_reg;
166                        current_reg = current_reg.next_index();
167
168                        instrs.push(Instruction::RowDef {
169                            insert_index: insert_reg_index,
170                            row_index: row_reg,
171                        });
172
173                        for value in row {
174                            instrs.push(Instruction::AddValue {
175                                row_index: row_reg,
176                                expr: value.try_into()?,
177                            });
178                        }
179                    }
180                    Ok(())
181                }
182                _ => Err(CodegenError::UnsupportedStatementForm(
183                    "Only insert with values is supported.",
184                    ast.to_string(),
185                )),
186            }?;
187
188            instrs.push(Instruction::Insert {
189                index: insert_reg_index,
190            });
191
192            Ok(())
193        }
194        Statement::Query(query) => {
195            // TODO: support CTEs
196            let mut table_reg_index = current_reg;
197            current_reg = current_reg.next_index();
198
199            match &query.body {
200                SetExpr::Select(select) => {
201                    match select.from.as_slice() {
202                        [TableWithJoins { relation, joins }] => {
203                            if !joins.is_empty() {
204                                // TODO: joins
205                                return Err(CodegenError::UnsupportedStatementForm(
206                                    "Joins are not supported yet",
207                                    select.to_string(),
208                                ));
209                            }
210
211                            match relation {
212                                TableFactor::Table {
213                                    name,
214                                    // TODO: support table alias
215                                    alias: _,
216                                    args: _,
217                                    with_hints: _,
218                                } => instrs.push(Instruction::Source {
219                                    index: table_reg_index,
220                                    name: name.0.clone().try_into()?,
221                                }),
222                                TableFactor::Derived { .. } => {
223                                    // TODO: support tables derived from a query
224                                    return Err(CodegenError::UnsupportedStatementForm(
225                                        "Derived tables are not supportd yet",
226                                        relation.to_string(),
227                                    ));
228                                }
229                                TableFactor::NestedJoin(_) => {
230                                    // TODO: support nested joins
231                                    return Err(CodegenError::UnsupportedStatementForm(
232                                        "Nested JOINs are not supportd yet",
233                                        relation.to_string(),
234                                    ));
235                                }
236                                TableFactor::TableFunction { .. } => {
237                                    // no plans to support these yet
238                                    return Err(CodegenError::UnsupportedStatementForm(
239                                        "Table functions are not supportd yet",
240                                        relation.to_string(),
241                                    ));
242                                }
243                                TableFactor::UNNEST { .. } => {
244                                    // no plans to support these yet
245                                    return Err(CodegenError::UnsupportedStatementForm(
246                                        "UNNEST are not supportd yet",
247                                        relation.to_string(),
248                                    ));
249                                }
250                            }
251                        }
252                        &[] => instrs.push(Instruction::NonExistent {
253                            index: table_reg_index,
254                        }),
255                        _ => {
256                            // TODO: multiple tables
257                            return Err(CodegenError::UnsupportedStatementForm(
258                                "Only select from a single table is supported for now",
259                                format!("{:?}", select.from),
260                            ));
261                        }
262                    }
263
264                    if let Some(expr) = select.selection.clone() {
265                        instrs.push(Instruction::Filter {
266                            index: table_reg_index,
267                            expr: expr.try_into()?,
268                        })
269                    }
270
271                    for group_by in select.group_by.clone() {
272                        instrs.push(Instruction::GroupBy {
273                            index: table_reg_index,
274                            expr: group_by.try_into()?,
275                        });
276                    }
277
278                    if let Some(expr) = select.having.clone() {
279                        instrs.push(Instruction::Filter {
280                            index: table_reg_index,
281                            expr: expr.try_into()?,
282                        })
283                    }
284
285                    if !select.projection.is_empty() {
286                        let original_table_reg_index = table_reg_index;
287                        table_reg_index = current_reg;
288                        current_reg = current_reg.next_index();
289
290                        instrs.push(Instruction::Empty {
291                            index: table_reg_index,
292                        });
293
294                        for projection in select.projection.clone() {
295                            instrs.push(Instruction::Project {
296                                input: original_table_reg_index,
297                                output: table_reg_index,
298                                expr: match projection {
299                                    SelectItem::UnnamedExpr(ref expr) => expr.clone().try_into()?,
300                                    SelectItem::ExprWithAlias { ref expr, .. } => {
301                                        expr.clone().try_into()?
302                                    }
303                                    SelectItem::QualifiedWildcard(_) => Expr::Wildcard,
304                                    SelectItem::Wildcard => Expr::Wildcard,
305                                },
306                                alias: match projection {
307                                    SelectItem::UnnamedExpr(_) => None,
308                                    SelectItem::ExprWithAlias { alias, .. } => {
309                                        Some(alias.value.as_str().into())
310                                    }
311                                    SelectItem::QualifiedWildcard(name) => {
312                                        return Err(CodegenError::UnsupportedStatementForm(
313                                            "Qualified wildcards are not supported yet",
314                                            name.to_string(),
315                                        ))
316                                    }
317                                    SelectItem::Wildcard => None,
318                                },
319                            })
320                        }
321
322                        if select.distinct {
323                            return Err(CodegenError::UnsupportedStatementForm(
324                                "DISTINCT is not supported yet",
325                                select.to_string(),
326                            ));
327                        }
328                    }
329                }
330                SetExpr::Values(exprs) => {
331                    if exprs.0.len() == 1 && exprs.0[0].len() == 1 {
332                        let expr: Expr = exprs.0[0][0].clone().try_into()?;
333                        instrs.push(Instruction::Expr {
334                            index: table_reg_index,
335                            expr,
336                        });
337                    } else {
338                        // TODO: selecting multiple values.
339                        //       the problem here is creating a temp table
340                        //       without information about column names
341                        //       and (more importantly) types.
342                        return Err(CodegenError::UnsupportedStatementForm(
343                            "Selecting more than one value is not supported yet",
344                            exprs.to_string(),
345                        ));
346                    }
347                }
348                SetExpr::Query(query) => {
349                    // TODO: figure out what syntax this corresponds to
350                    //       and implement it if necessary
351                    return Err(CodegenError::UnsupportedStatementForm(
352                        "Query within a query not supported yet",
353                        query.to_string(),
354                    ));
355                }
356                SetExpr::SetOperation {
357                    op: _,
358                    all: _,
359                    left: _,
360                    right: _,
361                } => {
362                    // TODO: set operations
363                    return Err(CodegenError::UnsupportedStatementForm(
364                        "Set operations are not supported yet",
365                        query.to_string(),
366                    ));
367                }
368                SetExpr::Insert(insert) => {
369                    // TODO: figure out what syntax this corresponds to
370                    //       and implement it if necessary
371                    return Err(CodegenError::UnsupportedStatementForm(
372                        "Insert within query not supported yet",
373                        insert.to_string(),
374                    ));
375                }
376            };
377
378            for order_by in query.order_by.clone() {
379                instrs.push(Instruction::Order {
380                    index: table_reg_index,
381                    expr: order_by.expr.try_into()?,
382                    ascending: order_by.asc.unwrap_or(true),
383                });
384                // TODO: support NULLS FIRST/NULLS LAST
385            }
386
387            if let Some(limit) = query.limit.clone() {
388                if let ast::Expr::Value(val) = limit.clone() {
389                    if let Value::Int64(limit) = val.clone().try_into()? {
390                        instrs.push(Instruction::Limit {
391                            index: table_reg_index,
392                            limit: limit as u64,
393                        });
394                    } else {
395                        // TODO: what are non constant limits anyway?
396                        return Err(CodegenError::Expr(ExprError::Value(ValueError {
397                            reason: "Only constant integer LIMITs are supported",
398                            value: val,
399                        })));
400                    }
401                } else {
402                    // TODO: what are non constant limits anyway?
403                    return Err(CodegenError::Expr(ExprError::Expr {
404                        reason: "Only constant integer LIMITs are supported",
405                        expr: limit,
406                    }));
407                }
408            }
409
410            instrs.push(Instruction::Return {
411                index: table_reg_index,
412            });
413
414            Ok(())
415        }
416        Statement::CreateSchema {
417            schema_name,
418            if_not_exists,
419        } => {
420            instrs.push(Instruction::NewSchema {
421                schema_name: schema_name.0.clone().try_into()?,
422                exists_ok: *if_not_exists,
423            });
424            Ok(())
425        }
426        _ => Err(CodegenError::UnsupportedStatement(ast.to_string())),
427    }?;
428
429    Ok(IntermediateCode { instrs })
430}
431
432/// Error while generating an intermediate code from the AST.
433#[derive(Debug)]
434pub enum CodegenError {
435    UnsupportedStatement(String),
436    UnsupportedStatementForm(&'static str, String),
437    InvalidIdentifier(IdentifierError),
438    Expr(ExprError),
439}
440
441impl Display for CodegenError {
442    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
443        match self {
444            CodegenError::UnsupportedStatement(s) => write!(f, "Unsupported statement: {}", s),
445            CodegenError::InvalidIdentifier(i) => {
446                write!(f, "{}", i)
447            }
448            CodegenError::UnsupportedStatementForm(reason, statement) => {
449                write!(f, "Unsupported: {} (Got: '{}')", reason, statement)
450            }
451            CodegenError::Expr(e) => write!(f, "{}", e),
452        }
453    }
454}
455
456impl From<IdentifierError> for CodegenError {
457    fn from(i: IdentifierError) -> Self {
458        CodegenError::InvalidIdentifier(i)
459    }
460}
461
462impl From<ExprError> for CodegenError {
463    fn from(e: ExprError) -> Self {
464        CodegenError::Expr(e)
465    }
466}
467
468impl From<ValueError> for CodegenError {
469    fn from(v: ValueError) -> Self {
470        CodegenError::Expr(v.into())
471    }
472}
473
474impl Error for CodegenError {}
475
476#[cfg(test)]
477mod tests {
478    use sqlparser::ast::{ColumnOption, ColumnOptionDef, DataType};
479
480    use pretty_assertions::assert_eq;
481
482    use crate::{
483        codegen::codegen_ast,
484        expr::{BinOp, Expr},
485        ic::Instruction,
486        identifier::{ColumnRef, SchemaRef, TableRef},
487        parser::parse,
488        value::Value,
489        vm::RegisterIndex,
490    };
491
492    fn check_single_statement(query: &str, callback: impl Fn(&[Instruction])) {
493        let parsed = parse(query).unwrap();
494        assert_eq!(parsed.len(), 1);
495
496        let statement = &parsed[0];
497        let ic = codegen_ast(&statement).unwrap();
498        callback(ic.instrs.as_slice());
499    }
500
501    #[test]
502    fn create_schema() {
503        check_single_statement("CREATE SCHEMA abc", |instrs| {
504            assert_eq!(
505                instrs,
506                &[Instruction::NewSchema {
507                    schema_name: SchemaRef("abc".into()),
508                    exists_ok: false,
509                }]
510            )
511        });
512
513        check_single_statement("CREATE SCHEMA IF NOT EXISTS abcd", |instrs| {
514            assert_eq!(
515                instrs,
516                &[Instruction::NewSchema {
517                    schema_name: SchemaRef("abcd".into()),
518                    exists_ok: true,
519                }]
520            )
521        });
522    }
523
524    #[test]
525    fn create_table() {
526        check_single_statement(
527            "CREATE TABLE
528             IF NOT EXISTS table1
529             (
530                 col1 INTEGER PRIMARY KEY NOT NULL,
531                 col2 STRING NOT NULL,
532                 col3 INTEGER UNIQUE
533             )",
534            |instrs| {
535                assert_eq!(
536                    instrs,
537                    &[
538                        Instruction::Empty {
539                            index: RegisterIndex::default()
540                        },
541                        Instruction::ColumnDef {
542                            index: RegisterIndex::default().next_index(),
543                            name: "col1".into(),
544                            data_type: DataType::Int(None),
545                        },
546                        Instruction::AddColumnOption {
547                            index: RegisterIndex::default().next_index(),
548                            option: ColumnOptionDef {
549                                name: None,
550                                option: ColumnOption::Unique { is_primary: true }
551                            }
552                        },
553                        Instruction::AddColumnOption {
554                            index: RegisterIndex::default().next_index(),
555                            option: ColumnOptionDef {
556                                name: None,
557                                option: ColumnOption::NotNull
558                            }
559                        },
560                        Instruction::AddColumn {
561                            table_reg_index: RegisterIndex::default(),
562                            col_index: RegisterIndex::default().next_index(),
563                        },
564                        Instruction::ColumnDef {
565                            index: RegisterIndex::default().next_index(),
566                            name: "col2".into(),
567                            data_type: DataType::String,
568                        },
569                        Instruction::AddColumnOption {
570                            index: RegisterIndex::default().next_index(),
571                            option: ColumnOptionDef {
572                                name: None,
573                                option: ColumnOption::NotNull
574                            }
575                        },
576                        Instruction::AddColumn {
577                            table_reg_index: RegisterIndex::default(),
578                            col_index: RegisterIndex::default().next_index(),
579                        },
580                        Instruction::ColumnDef {
581                            index: RegisterIndex::default().next_index(),
582                            name: "col3".into(),
583                            data_type: DataType::Int(None),
584                        },
585                        Instruction::AddColumnOption {
586                            index: RegisterIndex::default().next_index(),
587                            option: ColumnOptionDef {
588                                name: None,
589                                option: ColumnOption::Unique { is_primary: false }
590                            }
591                        },
592                        Instruction::AddColumn {
593                            table_reg_index: RegisterIndex::default(),
594                            col_index: RegisterIndex::default().next_index(),
595                        },
596                        Instruction::NewTable {
597                            index: RegisterIndex::default(),
598                            name: TableRef {
599                                schema_name: None,
600                                table_name: "table1".into()
601                            },
602                            exists_ok: true,
603                        }
604                    ]
605                )
606            },
607        );
608    }
609
610    #[test]
611    fn insert_values() {
612        check_single_statement(
613            "
614            INSERT INTO table1 VALUES
615                (2, 'bar'),
616                (3, 'baz')
617            ",
618            |instrs| {
619                assert_eq!(
620                    instrs,
621                    &[
622                        Instruction::Source {
623                            index: RegisterIndex::default(),
624                            name: TableRef {
625                                schema_name: None,
626                                table_name: "table1".into()
627                            }
628                        },
629                        Instruction::InsertDef {
630                            table_reg_index: RegisterIndex::default(),
631                            index: RegisterIndex::default().next_index(),
632                        },
633                        Instruction::RowDef {
634                            insert_index: RegisterIndex::default().next_index(),
635                            row_index: RegisterIndex::default().next_index().next_index(),
636                        },
637                        Instruction::AddValue {
638                            row_index: RegisterIndex::default().next_index().next_index(),
639                            expr: Expr::Value(Value::Int64(2)),
640                        },
641                        Instruction::AddValue {
642                            row_index: RegisterIndex::default().next_index().next_index(),
643                            expr: Expr::Value(Value::String("bar".to_owned())),
644                        },
645                        Instruction::RowDef {
646                            insert_index: RegisterIndex::default().next_index(),
647                            row_index: RegisterIndex::default()
648                                .next_index()
649                                .next_index()
650                                .next_index(),
651                        },
652                        Instruction::AddValue {
653                            row_index: RegisterIndex::default()
654                                .next_index()
655                                .next_index()
656                                .next_index(),
657                            expr: Expr::Value(Value::Int64(3)),
658                        },
659                        Instruction::AddValue {
660                            row_index: RegisterIndex::default()
661                                .next_index()
662                                .next_index()
663                                .next_index(),
664                            expr: Expr::Value(Value::String("baz".to_owned())),
665                        },
666                        Instruction::Insert {
667                            index: RegisterIndex::default().next_index()
668                        },
669                    ]
670                )
671            },
672        );
673
674        check_single_statement(
675            "
676            INSERT INTO table1 (col1, col2) VALUES
677                (2, 'bar'),
678                (3, 'baz')
679            ",
680            |instrs| {
681                assert_eq!(
682                    instrs,
683                    &[
684                        Instruction::Source {
685                            index: RegisterIndex::default(),
686                            name: TableRef {
687                                schema_name: None,
688                                table_name: "table1".into()
689                            }
690                        },
691                        Instruction::InsertDef {
692                            table_reg_index: RegisterIndex::default(),
693                            index: RegisterIndex::default().next_index(),
694                        },
695                        Instruction::ColumnInsertDef {
696                            insert_index: RegisterIndex::default().next_index(),
697                            col_name: "col1".into(),
698                        },
699                        Instruction::ColumnInsertDef {
700                            insert_index: RegisterIndex::default().next_index(),
701                            col_name: "col2".into(),
702                        },
703                        Instruction::RowDef {
704                            insert_index: RegisterIndex::default().next_index(),
705                            row_index: RegisterIndex::default().next_index().next_index(),
706                        },
707                        Instruction::AddValue {
708                            row_index: RegisterIndex::default().next_index().next_index(),
709                            expr: Expr::Value(Value::Int64(2)),
710                        },
711                        Instruction::AddValue {
712                            row_index: RegisterIndex::default().next_index().next_index(),
713                            expr: Expr::Value(Value::String("bar".to_owned())),
714                        },
715                        Instruction::RowDef {
716                            insert_index: RegisterIndex::default().next_index(),
717                            row_index: RegisterIndex::default()
718                                .next_index()
719                                .next_index()
720                                .next_index(),
721                        },
722                        Instruction::AddValue {
723                            row_index: RegisterIndex::default()
724                                .next_index()
725                                .next_index()
726                                .next_index(),
727                            expr: Expr::Value(Value::Int64(3)),
728                        },
729                        Instruction::AddValue {
730                            row_index: RegisterIndex::default()
731                                .next_index()
732                                .next_index()
733                                .next_index(),
734                            expr: Expr::Value(Value::String("baz".to_owned())),
735                        },
736                        Instruction::Insert {
737                            index: RegisterIndex::default().next_index()
738                        },
739                    ]
740                )
741            },
742        );
743    }
744
745    #[test]
746    fn select() {
747        check_single_statement("SELECT 1", |instrs| {
748            assert_eq!(
749                instrs,
750                &[
751                    Instruction::NonExistent {
752                        index: RegisterIndex::default()
753                    },
754                    Instruction::Empty {
755                        index: RegisterIndex::default().next_index()
756                    },
757                    Instruction::Project {
758                        input: RegisterIndex::default(),
759                        output: RegisterIndex::default().next_index(),
760                        expr: Expr::Value(Value::Int64(1)),
761                        alias: None
762                    },
763                    Instruction::Return {
764                        index: RegisterIndex::default().next_index()
765                    },
766                ]
767            )
768        });
769
770        check_single_statement("SELECT * FROM table1", |instrs| {
771            assert_eq!(
772                instrs,
773                &[
774                    Instruction::Source {
775                        index: RegisterIndex::default(),
776                        name: TableRef {
777                            schema_name: None,
778                            table_name: "table1".into()
779                        }
780                    },
781                    Instruction::Empty {
782                        index: RegisterIndex::default().next_index()
783                    },
784                    Instruction::Project {
785                        input: RegisterIndex::default(),
786                        output: RegisterIndex::default().next_index(),
787                        expr: Expr::Wildcard,
788                        alias: None
789                    },
790                    Instruction::Return {
791                        index: RegisterIndex::default().next_index(),
792                    }
793                ]
794            )
795        });
796
797        check_single_statement("SELECT * FROM table1 WHERE col1 = 1", |instrs| {
798            assert_eq!(
799                instrs,
800                &[
801                    Instruction::Source {
802                        index: RegisterIndex::default(),
803                        name: TableRef {
804                            schema_name: None,
805                            table_name: "table1".into()
806                        }
807                    },
808                    Instruction::Filter {
809                        index: RegisterIndex::default(),
810                        expr: Expr::Binary {
811                            left: Box::new(Expr::ColumnRef(ColumnRef {
812                                schema_name: None,
813                                table_name: None,
814                                col_name: "col1".into(),
815                            },)),
816                            op: BinOp::Equal,
817                            right: Box::new(Expr::Value(Value::Int64(1)))
818                        },
819                    },
820                    Instruction::Empty {
821                        index: RegisterIndex::default().next_index()
822                    },
823                    Instruction::Project {
824                        input: RegisterIndex::default(),
825                        output: RegisterIndex::default().next_index(),
826                        expr: Expr::Wildcard,
827                        alias: None
828                    },
829                    Instruction::Return {
830                        index: RegisterIndex::default().next_index(),
831                    }
832                ]
833            )
834        });
835
836        check_single_statement(
837            "SELECT col2, col3
838            FROM table1
839            WHERE col1 = 1
840            ",
841            |instrs| {
842                assert_eq!(
843                    instrs,
844                    &[
845                        Instruction::Source {
846                            index: RegisterIndex::default(),
847                            name: TableRef {
848                                schema_name: None,
849                                table_name: "table1".into()
850                            }
851                        },
852                        Instruction::Filter {
853                            index: RegisterIndex::default(),
854                            expr: Expr::Binary {
855                                left: Box::new(Expr::ColumnRef(ColumnRef {
856                                    schema_name: None,
857                                    table_name: None,
858                                    col_name: "col1".into(),
859                                },)),
860                                op: BinOp::Equal,
861                                right: Box::new(Expr::Value(Value::Int64(1)))
862                            },
863                        },
864                        Instruction::Empty {
865                            index: RegisterIndex::default().next_index()
866                        },
867                        Instruction::Project {
868                            input: RegisterIndex::default(),
869                            output: RegisterIndex::default().next_index(),
870                            expr: Expr::ColumnRef(ColumnRef {
871                                schema_name: None,
872                                table_name: None,
873                                col_name: "col2".into(),
874                            }),
875                            alias: None
876                        },
877                        Instruction::Project {
878                            input: RegisterIndex::default(),
879                            output: RegisterIndex::default().next_index(),
880                            expr: Expr::ColumnRef(ColumnRef {
881                                schema_name: None,
882                                table_name: None,
883                                col_name: "col3".into(),
884                            }),
885                            alias: None
886                        },
887                        Instruction::Return {
888                            index: RegisterIndex::default().next_index(),
889                        }
890                    ]
891                )
892            },
893        );
894
895        check_single_statement(
896            "SELECT col2, col3
897            FROM table1
898            WHERE col1 = 1 AND col2 = 2
899            ",
900            |instrs| {
901                assert_eq!(
902                    instrs,
903                    &[
904                        Instruction::Source {
905                            index: RegisterIndex::default(),
906                            name: TableRef {
907                                schema_name: None,
908                                table_name: "table1".into()
909                            }
910                        },
911                        Instruction::Filter {
912                            index: RegisterIndex::default(),
913                            expr: Expr::Binary {
914                                left: Box::new(Expr::Binary {
915                                    left: Box::new(Expr::ColumnRef(ColumnRef {
916                                        schema_name: None,
917                                        table_name: None,
918                                        col_name: "col1".into(),
919                                    },)),
920                                    op: BinOp::Equal,
921                                    right: Box::new(Expr::Value(Value::Int64(1)))
922                                }),
923                                op: BinOp::And,
924                                right: Box::new(Expr::Binary {
925                                    left: Box::new(Expr::ColumnRef(ColumnRef {
926                                        schema_name: None,
927                                        table_name: None,
928                                        col_name: "col2".into(),
929                                    },)),
930                                    op: BinOp::Equal,
931                                    right: Box::new(Expr::Value(Value::Int64(2)))
932                                })
933                            },
934                        },
935                        Instruction::Empty {
936                            index: RegisterIndex::default().next_index()
937                        },
938                        Instruction::Project {
939                            input: RegisterIndex::default(),
940                            output: RegisterIndex::default().next_index(),
941                            expr: Expr::ColumnRef(ColumnRef {
942                                schema_name: None,
943                                table_name: None,
944                                col_name: "col2".into(),
945                            }),
946                            alias: None
947                        },
948                        Instruction::Project {
949                            input: RegisterIndex::default(),
950                            output: RegisterIndex::default().next_index(),
951                            expr: Expr::ColumnRef(ColumnRef {
952                                schema_name: None,
953                                table_name: None,
954                                col_name: "col3".into(),
955                            }),
956                            alias: None
957                        },
958                        Instruction::Return {
959                            index: RegisterIndex::default().next_index(),
960                        }
961                    ]
962                )
963            },
964        );
965
966        check_single_statement(
967            "SELECT col2, col3
968            FROM table1
969            WHERE col1 = 1 OR col2 = 2
970            ",
971            |instrs| {
972                assert_eq!(
973                    instrs,
974                    &[
975                        Instruction::Source {
976                            index: RegisterIndex::default(),
977                            name: TableRef {
978                                schema_name: None,
979                                table_name: "table1".into()
980                            }
981                        },
982                        Instruction::Filter {
983                            index: RegisterIndex::default(),
984                            expr: Expr::Binary {
985                                left: Box::new(Expr::Binary {
986                                    left: Box::new(Expr::ColumnRef(ColumnRef {
987                                        schema_name: None,
988                                        table_name: None,
989                                        col_name: "col1".into(),
990                                    },)),
991                                    op: BinOp::Equal,
992                                    right: Box::new(Expr::Value(Value::Int64(1)))
993                                }),
994                                op: BinOp::Or,
995                                right: Box::new(Expr::Binary {
996                                    left: Box::new(Expr::ColumnRef(ColumnRef {
997                                        schema_name: None,
998                                        table_name: None,
999                                        col_name: "col2".into(),
1000                                    },)),
1001                                    op: BinOp::Equal,
1002                                    right: Box::new(Expr::Value(Value::Int64(2)))
1003                                })
1004                            },
1005                        },
1006                        Instruction::Empty {
1007                            index: RegisterIndex::default().next_index()
1008                        },
1009                        Instruction::Project {
1010                            input: RegisterIndex::default(),
1011                            output: RegisterIndex::default().next_index(),
1012                            expr: Expr::ColumnRef(ColumnRef {
1013                                schema_name: None,
1014                                table_name: None,
1015                                col_name: "col2".into(),
1016                            }),
1017                            alias: None
1018                        },
1019                        Instruction::Project {
1020                            input: RegisterIndex::default(),
1021                            output: RegisterIndex::default().next_index(),
1022                            expr: Expr::ColumnRef(ColumnRef {
1023                                schema_name: None,
1024                                table_name: None,
1025                                col_name: "col3".into(),
1026                            }),
1027                            alias: None
1028                        },
1029                        Instruction::Return {
1030                            index: RegisterIndex::default().next_index(),
1031                        }
1032                    ]
1033                )
1034            },
1035        );
1036
1037        check_single_statement(
1038            "SELECT col2, col3
1039            FROM table1
1040            WHERE col1 = 1
1041            ORDER BY col2
1042            LIMIT 100
1043            ",
1044            |instrs| {
1045                assert_eq!(
1046                    instrs,
1047                    &[
1048                        Instruction::Source {
1049                            index: RegisterIndex::default(),
1050                            name: TableRef {
1051                                schema_name: None,
1052                                table_name: "table1".into()
1053                            }
1054                        },
1055                        Instruction::Filter {
1056                            index: RegisterIndex::default(),
1057                            expr: Expr::Binary {
1058                                left: Box::new(Expr::ColumnRef(ColumnRef {
1059                                    schema_name: None,
1060                                    table_name: None,
1061                                    col_name: "col1".into(),
1062                                },)),
1063                                op: BinOp::Equal,
1064                                right: Box::new(Expr::Value(Value::Int64(1)))
1065                            },
1066                        },
1067                        Instruction::Empty {
1068                            index: RegisterIndex::default().next_index()
1069                        },
1070                        Instruction::Project {
1071                            input: RegisterIndex::default(),
1072                            output: RegisterIndex::default().next_index(),
1073                            expr: Expr::ColumnRef(ColumnRef {
1074                                schema_name: None,
1075                                table_name: None,
1076                                col_name: "col2".into(),
1077                            }),
1078                            alias: None
1079                        },
1080                        Instruction::Project {
1081                            input: RegisterIndex::default(),
1082                            output: RegisterIndex::default().next_index(),
1083                            expr: Expr::ColumnRef(ColumnRef {
1084                                schema_name: None,
1085                                table_name: None,
1086                                col_name: "col3".into(),
1087                            }),
1088                            alias: None
1089                        },
1090                        Instruction::Order {
1091                            index: RegisterIndex::default().next_index(),
1092                            expr: Expr::ColumnRef(ColumnRef {
1093                                schema_name: None,
1094                                table_name: None,
1095                                col_name: "col2".into(),
1096                            }),
1097                            ascending: true
1098                        },
1099                        Instruction::Limit {
1100                            index: RegisterIndex::default().next_index(),
1101                            limit: 100,
1102                        },
1103                        Instruction::Return {
1104                            index: RegisterIndex::default().next_index(),
1105                        }
1106                    ]
1107                )
1108            },
1109        );
1110
1111        check_single_statement(
1112            "SELECT col2, MAX(col3) AS max_col3
1113            FROM table1
1114            WHERE col1 = 1
1115            GROUP BY col2
1116            HAVING MAX(col3) > 10
1117            ",
1118            |instrs| {
1119                assert_eq!(
1120                    instrs,
1121                    &[
1122                        Instruction::Source {
1123                            index: RegisterIndex::default(),
1124                            name: TableRef {
1125                                schema_name: None,
1126                                table_name: "table1".into()
1127                            }
1128                        },
1129                        Instruction::Filter {
1130                            index: RegisterIndex::default(),
1131                            expr: Expr::Binary {
1132                                left: Box::new(Expr::ColumnRef(ColumnRef {
1133                                    schema_name: None,
1134                                    table_name: None,
1135                                    col_name: "col1".into(),
1136                                },)),
1137                                op: BinOp::Equal,
1138                                right: Box::new(Expr::Value(Value::Int64(1)))
1139                            },
1140                        },
1141                        Instruction::GroupBy {
1142                            index: RegisterIndex::default(),
1143                            expr: Expr::ColumnRef(ColumnRef {
1144                                schema_name: None,
1145                                table_name: None,
1146                                col_name: "col2".into(),
1147                            })
1148                        },
1149                        Instruction::Filter {
1150                            index: RegisterIndex::default(),
1151                            expr: Expr::Binary {
1152                                left: Box::new(Expr::Function {
1153                                    name: "MAX".into(),
1154                                    args: vec![Expr::ColumnRef(ColumnRef {
1155                                        schema_name: None,
1156                                        table_name: None,
1157                                        col_name: "col3".into(),
1158                                    })]
1159                                }),
1160                                op: BinOp::GreaterThan,
1161                                right: Box::new(Expr::Value(Value::Int64(10)))
1162                            },
1163                        },
1164                        Instruction::Empty {
1165                            index: RegisterIndex::default().next_index()
1166                        },
1167                        Instruction::Project {
1168                            input: RegisterIndex::default(),
1169                            output: RegisterIndex::default().next_index(),
1170                            expr: Expr::ColumnRef(ColumnRef {
1171                                schema_name: None,
1172                                table_name: None,
1173                                col_name: "col2".into(),
1174                            }),
1175                            alias: None
1176                        },
1177                        Instruction::Project {
1178                            input: RegisterIndex::default(),
1179                            output: RegisterIndex::default().next_index(),
1180                            expr: Expr::Function {
1181                                name: "MAX".into(),
1182                                args: vec![Expr::ColumnRef(ColumnRef {
1183                                    schema_name: None,
1184                                    table_name: None,
1185                                    col_name: "col3".into(),
1186                                })]
1187                            },
1188                            alias: Some("max_col3".into())
1189                        },
1190                        Instruction::Return {
1191                            index: RegisterIndex::default().next_index(),
1192                        }
1193                    ]
1194                )
1195            },
1196        );
1197    }
1198}