mirror of
				https://github.com/apache/datafusion-sqlparser-rs.git
				synced 2025-11-04 00:43:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			811 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			811 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
// http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
#[macro_use]
 | 
						|
mod test_utils;
 | 
						|
 | 
						|
use test_utils::*;
 | 
						|
 | 
						|
use sqlparser::ast::*;
 | 
						|
use sqlparser::dialect::{DuckDbDialect, GenericDialect};
 | 
						|
 | 
						|
fn duckdb() -> TestedDialects {
 | 
						|
    TestedDialects {
 | 
						|
        dialects: vec![Box::new(DuckDbDialect {})],
 | 
						|
        options: None,
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn duckdb_and_generic() -> TestedDialects {
 | 
						|
    TestedDialects {
 | 
						|
        dialects: vec![Box::new(DuckDbDialect {}), Box::new(GenericDialect {})],
 | 
						|
        options: None,
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_struct() {
 | 
						|
    // s STRUCT(v VARCHAR, i INTEGER)
 | 
						|
    let struct_type1 = DataType::Struct(
 | 
						|
        vec![
 | 
						|
            StructField {
 | 
						|
                field_name: Some(Ident::new("v")),
 | 
						|
                field_type: DataType::Varchar(None),
 | 
						|
            },
 | 
						|
            StructField {
 | 
						|
                field_name: Some(Ident::new("i")),
 | 
						|
                field_type: DataType::Integer(None),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
        StructBracketKind::Parentheses,
 | 
						|
    );
 | 
						|
 | 
						|
    // basic struct
 | 
						|
    let statement = duckdb().verified_stmt(r#"CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER))"#);
 | 
						|
    assert_eq!(
 | 
						|
        column_defs(statement),
 | 
						|
        vec![ColumnDef {
 | 
						|
            name: "s".into(),
 | 
						|
            data_type: struct_type1.clone(),
 | 
						|
            collation: None,
 | 
						|
            options: vec![],
 | 
						|
        }]
 | 
						|
    );
 | 
						|
 | 
						|
    // struct array
 | 
						|
    let statement = duckdb().verified_stmt(r#"CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER)[])"#);
 | 
						|
    assert_eq!(
 | 
						|
        column_defs(statement),
 | 
						|
        vec![ColumnDef {
 | 
						|
            name: "s".into(),
 | 
						|
            data_type: DataType::Array(ArrayElemTypeDef::SquareBracket(
 | 
						|
                Box::new(struct_type1),
 | 
						|
                None
 | 
						|
            )),
 | 
						|
            collation: None,
 | 
						|
            options: vec![],
 | 
						|
        }]
 | 
						|
    );
 | 
						|
 | 
						|
    // s STRUCT(v VARCHAR, s STRUCT(a1 INTEGER, a2 VARCHAR))
 | 
						|
    let struct_type2 = DataType::Struct(
 | 
						|
        vec![
 | 
						|
            StructField {
 | 
						|
                field_name: Some(Ident::new("v")),
 | 
						|
                field_type: DataType::Varchar(None),
 | 
						|
            },
 | 
						|
            StructField {
 | 
						|
                field_name: Some(Ident::new("s")),
 | 
						|
                field_type: DataType::Struct(
 | 
						|
                    vec![
 | 
						|
                        StructField {
 | 
						|
                            field_name: Some(Ident::new("a1")),
 | 
						|
                            field_type: DataType::Integer(None),
 | 
						|
                        },
 | 
						|
                        StructField {
 | 
						|
                            field_name: Some(Ident::new("a2")),
 | 
						|
                            field_type: DataType::Varchar(None),
 | 
						|
                        },
 | 
						|
                    ],
 | 
						|
                    StructBracketKind::Parentheses,
 | 
						|
                ),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
        StructBracketKind::Parentheses,
 | 
						|
    );
 | 
						|
 | 
						|
    // nested struct
 | 
						|
    let statement = duckdb().verified_stmt(
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT(v VARCHAR, s STRUCT(a1 INTEGER, a2 VARCHAR))[])"#,
 | 
						|
    );
 | 
						|
 | 
						|
    assert_eq!(
 | 
						|
        column_defs(statement),
 | 
						|
        vec![ColumnDef {
 | 
						|
            name: "s".into(),
 | 
						|
            data_type: DataType::Array(ArrayElemTypeDef::SquareBracket(
 | 
						|
                Box::new(struct_type2),
 | 
						|
                None
 | 
						|
            )),
 | 
						|
            collation: None,
 | 
						|
            options: vec![],
 | 
						|
        }]
 | 
						|
    );
 | 
						|
 | 
						|
    // failing test (duckdb does not support bracket syntax)
 | 
						|
    let sql_list = vec![
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER)))"#,
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER>)"#,
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT<v VARCHAR, i INTEGER>)"#,
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT v VARCHAR, i INTEGER )"#,
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT VARCHAR, i INTEGER )"#,
 | 
						|
        r#"CREATE TABLE t1 (s STRUCT (VARCHAR, INTEGER))"#,
 | 
						|
    ];
 | 
						|
 | 
						|
    for sql in sql_list {
 | 
						|
        duckdb().parse_sql_statements(sql).unwrap_err();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Returns the ColumnDefinitions from a CreateTable statement
 | 
						|
fn column_defs(statement: Statement) -> Vec<ColumnDef> {
 | 
						|
    match statement {
 | 
						|
        Statement::CreateTable(CreateTable { columns, .. }) => columns,
 | 
						|
        _ => panic!("Expected CreateTable"),
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_select_wildcard_with_exclude() {
 | 
						|
    let select = duckdb().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
 | 
						|
    let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
 | 
						|
        opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
 | 
						|
        ..Default::default()
 | 
						|
    });
 | 
						|
    assert_eq!(expected, select.projection[0]);
 | 
						|
 | 
						|
    let select =
 | 
						|
        duckdb().verified_only_select("SELECT name.* EXCLUDE department_id FROM employee_table");
 | 
						|
    let expected = SelectItem::QualifiedWildcard(
 | 
						|
        ObjectName(vec![Ident::new("name")]),
 | 
						|
        WildcardAdditionalOptions {
 | 
						|
            opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
 | 
						|
            ..Default::default()
 | 
						|
        },
 | 
						|
    );
 | 
						|
    assert_eq!(expected, select.projection[0]);
 | 
						|
 | 
						|
    let select = duckdb()
 | 
						|
        .verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
 | 
						|
    let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
 | 
						|
        opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
 | 
						|
            Ident::new("department_id"),
 | 
						|
            Ident::new("employee_id"),
 | 
						|
        ])),
 | 
						|
        ..Default::default()
 | 
						|
    });
 | 
						|
    assert_eq!(expected, select.projection[0]);
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn parse_div_infix() {
 | 
						|
    duckdb_and_generic().verified_stmt(r#"SELECT 5 // 2"#);
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_create_macro() {
 | 
						|
    let macro_ = duckdb().verified_stmt("CREATE MACRO schema.add(a, b) AS a + b");
 | 
						|
    let expected = Statement::CreateMacro {
 | 
						|
        or_replace: false,
 | 
						|
        temporary: false,
 | 
						|
        name: ObjectName(vec![Ident::new("schema"), Ident::new("add")]),
 | 
						|
        args: Some(vec![MacroArg::new("a"), MacroArg::new("b")]),
 | 
						|
        definition: MacroDefinition::Expr(Expr::BinaryOp {
 | 
						|
            left: Box::new(Expr::Identifier(Ident::new("a"))),
 | 
						|
            op: BinaryOperator::Plus,
 | 
						|
            right: Box::new(Expr::Identifier(Ident::new("b"))),
 | 
						|
        }),
 | 
						|
    };
 | 
						|
    assert_eq!(expected, macro_);
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_create_macro_default_args() {
 | 
						|
    let macro_ = duckdb().verified_stmt("CREATE MACRO add_default(a, b := 5) AS a + b");
 | 
						|
    let expected = Statement::CreateMacro {
 | 
						|
        or_replace: false,
 | 
						|
        temporary: false,
 | 
						|
        name: ObjectName(vec![Ident::new("add_default")]),
 | 
						|
        args: Some(vec![
 | 
						|
            MacroArg::new("a"),
 | 
						|
            MacroArg {
 | 
						|
                name: Ident::new("b"),
 | 
						|
                default_expr: Some(Expr::Value(number("5"))),
 | 
						|
            },
 | 
						|
        ]),
 | 
						|
        definition: MacroDefinition::Expr(Expr::BinaryOp {
 | 
						|
            left: Box::new(Expr::Identifier(Ident::new("a"))),
 | 
						|
            op: BinaryOperator::Plus,
 | 
						|
            right: Box::new(Expr::Identifier(Ident::new("b"))),
 | 
						|
        }),
 | 
						|
    };
 | 
						|
    assert_eq!(expected, macro_);
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_create_table_macro() {
 | 
						|
    let query = "SELECT col1_value AS column1, col2_value AS column2 UNION ALL SELECT 'Hello' AS col1_value, 456 AS col2_value";
 | 
						|
    let macro_ = duckdb().verified_stmt(
 | 
						|
        &("CREATE OR REPLACE TEMPORARY MACRO dynamic_table(col1_value, col2_value) AS TABLE "
 | 
						|
            .to_string()
 | 
						|
            + query),
 | 
						|
    );
 | 
						|
    let expected = Statement::CreateMacro {
 | 
						|
        or_replace: true,
 | 
						|
        temporary: true,
 | 
						|
        name: ObjectName(vec![Ident::new("dynamic_table")]),
 | 
						|
        args: Some(vec![
 | 
						|
            MacroArg::new("col1_value"),
 | 
						|
            MacroArg::new("col2_value"),
 | 
						|
        ]),
 | 
						|
        definition: MacroDefinition::Table(duckdb().verified_query(query)),
 | 
						|
    };
 | 
						|
    assert_eq!(expected, macro_);
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_select_union_by_name() {
 | 
						|
    let q1 = "SELECT * FROM capitals UNION BY NAME SELECT * FROM weather";
 | 
						|
    let q2 = "SELECT * FROM capitals UNION ALL BY NAME SELECT * FROM weather";
 | 
						|
    let q3 = "SELECT * FROM capitals UNION DISTINCT BY NAME SELECT * FROM weather";
 | 
						|
 | 
						|
    for (ast, expected_quantifier) in &[
 | 
						|
        (duckdb().verified_query(q1), SetQuantifier::ByName),
 | 
						|
        (duckdb().verified_query(q2), SetQuantifier::AllByName),
 | 
						|
        (duckdb().verified_query(q3), SetQuantifier::DistinctByName),
 | 
						|
    ] {
 | 
						|
        let expected = Box::<SetExpr>::new(SetExpr::SetOperation {
 | 
						|
            op: SetOperator::Union,
 | 
						|
            set_quantifier: *expected_quantifier,
 | 
						|
            left: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
 | 
						|
                distinct: None,
 | 
						|
                top: None,
 | 
						|
                projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
 | 
						|
                    opt_ilike: None,
 | 
						|
                    opt_exclude: None,
 | 
						|
                    opt_except: None,
 | 
						|
                    opt_rename: None,
 | 
						|
                    opt_replace: None,
 | 
						|
                })],
 | 
						|
                into: None,
 | 
						|
                from: vec![TableWithJoins {
 | 
						|
                    relation: TableFactor::Table {
 | 
						|
                        name: ObjectName(vec![Ident {
 | 
						|
                            value: "capitals".to_string(),
 | 
						|
                            quote_style: None,
 | 
						|
                        }]),
 | 
						|
                        alias: None,
 | 
						|
                        args: None,
 | 
						|
                        with_hints: vec![],
 | 
						|
                        version: None,
 | 
						|
                        partitions: vec![],
 | 
						|
                        with_ordinality: false,
 | 
						|
                    },
 | 
						|
                    joins: vec![],
 | 
						|
                }],
 | 
						|
                lateral_views: vec![],
 | 
						|
                prewhere: None,
 | 
						|
                selection: None,
 | 
						|
                group_by: GroupByExpr::Expressions(vec![], vec![]),
 | 
						|
                cluster_by: vec![],
 | 
						|
                distribute_by: vec![],
 | 
						|
                sort_by: vec![],
 | 
						|
                having: None,
 | 
						|
                named_window: vec![],
 | 
						|
                window_before_qualify: false,
 | 
						|
                qualify: None,
 | 
						|
                value_table_mode: None,
 | 
						|
                connect_by: None,
 | 
						|
            }))),
 | 
						|
            right: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
 | 
						|
                distinct: None,
 | 
						|
                top: None,
 | 
						|
                projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
 | 
						|
                    opt_ilike: None,
 | 
						|
                    opt_exclude: None,
 | 
						|
                    opt_except: None,
 | 
						|
                    opt_rename: None,
 | 
						|
                    opt_replace: None,
 | 
						|
                })],
 | 
						|
                into: None,
 | 
						|
                from: vec![TableWithJoins {
 | 
						|
                    relation: TableFactor::Table {
 | 
						|
                        name: ObjectName(vec![Ident {
 | 
						|
                            value: "weather".to_string(),
 | 
						|
                            quote_style: None,
 | 
						|
                        }]),
 | 
						|
                        alias: None,
 | 
						|
                        args: None,
 | 
						|
                        with_hints: vec![],
 | 
						|
                        version: None,
 | 
						|
                        partitions: vec![],
 | 
						|
                        with_ordinality: false,
 | 
						|
                    },
 | 
						|
                    joins: vec![],
 | 
						|
                }],
 | 
						|
                lateral_views: vec![],
 | 
						|
                prewhere: None,
 | 
						|
                selection: None,
 | 
						|
                group_by: GroupByExpr::Expressions(vec![], vec![]),
 | 
						|
                cluster_by: vec![],
 | 
						|
                distribute_by: vec![],
 | 
						|
                sort_by: vec![],
 | 
						|
                having: None,
 | 
						|
                named_window: vec![],
 | 
						|
                window_before_qualify: false,
 | 
						|
                qualify: None,
 | 
						|
                value_table_mode: None,
 | 
						|
                connect_by: None,
 | 
						|
            }))),
 | 
						|
        });
 | 
						|
        assert_eq!(ast.body, expected);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_duckdb_install() {
 | 
						|
    let stmt = duckdb().verified_stmt("INSTALL tpch");
 | 
						|
    assert_eq!(
 | 
						|
        stmt,
 | 
						|
        Statement::Install {
 | 
						|
            extension_name: Ident {
 | 
						|
                value: "tpch".to_string(),
 | 
						|
                quote_style: None
 | 
						|
            }
 | 
						|
        }
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_duckdb_load_extension() {
 | 
						|
    let stmt = duckdb().verified_stmt("LOAD my_extension");
 | 
						|
    assert_eq!(
 | 
						|
        Statement::Load {
 | 
						|
            extension_name: Ident {
 | 
						|
                value: "my_extension".to_string(),
 | 
						|
                quote_style: None
 | 
						|
            }
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_duckdb_struct_literal() {
 | 
						|
    //struct literal syntax https://duckdb.org/docs/sql/data_types/struct#creating-structs
 | 
						|
    //syntax: {'field_name': expr1[, ... ]}
 | 
						|
    let sql = "SELECT {'a': 1, 'b': 2, 'c': 3}, [{'a': 'abc'}], {'a': 1, 'b': [t.str_col]}, {'a': 1, 'b': 'abc'}, {'abc': str_col}, {'a': {'aa': 1}}";
 | 
						|
    let select = duckdb_and_generic().verified_only_select(sql);
 | 
						|
    assert_eq!(6, select.projection.len());
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Dictionary(vec![
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "a"),
 | 
						|
                value: Box::new(Expr::Value(number("1"))),
 | 
						|
            },
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "b"),
 | 
						|
                value: Box::new(Expr::Value(number("2"))),
 | 
						|
            },
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "c"),
 | 
						|
                value: Box::new(Expr::Value(number("3"))),
 | 
						|
            },
 | 
						|
        ],),
 | 
						|
        expr_from_projection(&select.projection[0])
 | 
						|
    );
 | 
						|
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Array(Array {
 | 
						|
            elem: vec![Expr::Dictionary(vec![DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "a"),
 | 
						|
                value: Box::new(Expr::Value(Value::SingleQuotedString("abc".to_string()))),
 | 
						|
            },],)],
 | 
						|
            named: false
 | 
						|
        }),
 | 
						|
        expr_from_projection(&select.projection[1])
 | 
						|
    );
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Dictionary(vec![
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "a"),
 | 
						|
                value: Box::new(Expr::Value(number("1"))),
 | 
						|
            },
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "b"),
 | 
						|
                value: Box::new(Expr::Array(Array {
 | 
						|
                    elem: vec![Expr::CompoundIdentifier(vec![
 | 
						|
                        Ident::from("t"),
 | 
						|
                        Ident::from("str_col")
 | 
						|
                    ])],
 | 
						|
                    named: false
 | 
						|
                })),
 | 
						|
            },
 | 
						|
        ],),
 | 
						|
        expr_from_projection(&select.projection[2])
 | 
						|
    );
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Dictionary(vec![
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "a"),
 | 
						|
                value: Expr::Value(number("1")).into(),
 | 
						|
            },
 | 
						|
            DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "b"),
 | 
						|
                value: Expr::Value(Value::SingleQuotedString("abc".to_string())).into(),
 | 
						|
            },
 | 
						|
        ],),
 | 
						|
        expr_from_projection(&select.projection[3])
 | 
						|
    );
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Dictionary(vec![DictionaryField {
 | 
						|
            key: Ident::with_quote('\'', "abc"),
 | 
						|
            value: Expr::Identifier(Ident::from("str_col")).into(),
 | 
						|
        }],),
 | 
						|
        expr_from_projection(&select.projection[4])
 | 
						|
    );
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Dictionary(vec![DictionaryField {
 | 
						|
            key: Ident::with_quote('\'', "a"),
 | 
						|
            value: Expr::Dictionary(vec![DictionaryField {
 | 
						|
                key: Ident::with_quote('\'', "aa"),
 | 
						|
                value: Expr::Value(number("1")).into(),
 | 
						|
            }],)
 | 
						|
            .into(),
 | 
						|
        }],),
 | 
						|
        expr_from_projection(&select.projection[5])
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_create_secret() {
 | 
						|
    let sql = r#"CREATE OR REPLACE PERSISTENT SECRET IF NOT EXISTS name IN storage ( TYPE type, key1 value1, key2 value2 )"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::CreateSecret {
 | 
						|
            or_replace: true,
 | 
						|
            temporary: Some(false),
 | 
						|
            if_not_exists: true,
 | 
						|
            name: Some(Ident::new("name")),
 | 
						|
            storage_specifier: Some(Ident::new("storage")),
 | 
						|
            secret_type: Ident::new("type"),
 | 
						|
            options: vec![
 | 
						|
                SecretOption {
 | 
						|
                    key: Ident::new("key1"),
 | 
						|
                    value: Ident::new("value1"),
 | 
						|
                },
 | 
						|
                SecretOption {
 | 
						|
                    key: Ident::new("key2"),
 | 
						|
                    value: Ident::new("value2"),
 | 
						|
                }
 | 
						|
            ]
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_create_secret_simple() {
 | 
						|
    let sql = r#"CREATE SECRET ( TYPE type )"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::CreateSecret {
 | 
						|
            or_replace: false,
 | 
						|
            temporary: None,
 | 
						|
            if_not_exists: false,
 | 
						|
            name: None,
 | 
						|
            storage_specifier: None,
 | 
						|
            secret_type: Ident::new("type"),
 | 
						|
            options: vec![]
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_drop_secret() {
 | 
						|
    let sql = r#"DROP PERSISTENT SECRET IF EXISTS secret FROM storage"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::DropSecret {
 | 
						|
            if_exists: true,
 | 
						|
            temporary: Some(false),
 | 
						|
            name: Ident::new("secret"),
 | 
						|
            storage_specifier: Some(Ident::new("storage"))
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_drop_secret_simple() {
 | 
						|
    let sql = r#"DROP SECRET secret"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::DropSecret {
 | 
						|
            if_exists: false,
 | 
						|
            temporary: None,
 | 
						|
            name: Ident::new("secret"),
 | 
						|
            storage_specifier: None
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_attach_database() {
 | 
						|
    let sql = r#"ATTACH DATABASE IF NOT EXISTS 'sqlite_file.db' AS sqlite_db (READ_ONLY false, TYPE SQLITE)"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::AttachDuckDBDatabase {
 | 
						|
            if_not_exists: true,
 | 
						|
            database: true,
 | 
						|
            database_path: Ident::with_quote('\'', "sqlite_file.db"),
 | 
						|
            database_alias: Some(Ident::new("sqlite_db")),
 | 
						|
            attach_options: vec![
 | 
						|
                AttachDuckDBDatabaseOption::ReadOnly(Some(false)),
 | 
						|
                AttachDuckDBDatabaseOption::Type(Ident::new("SQLITE")),
 | 
						|
            ]
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_attach_database_simple() {
 | 
						|
    let sql = r#"ATTACH 'postgres://user.name:pass-word@some.url.com:5432/postgres'"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::AttachDuckDBDatabase {
 | 
						|
            if_not_exists: false,
 | 
						|
            database: false,
 | 
						|
            database_path: Ident::with_quote(
 | 
						|
                '\'',
 | 
						|
                "postgres://user.name:pass-word@some.url.com:5432/postgres"
 | 
						|
            ),
 | 
						|
            database_alias: None,
 | 
						|
            attach_options: vec![]
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_detach_database() {
 | 
						|
    let sql = r#"DETACH DATABASE IF EXISTS db_name"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::DetachDuckDBDatabase {
 | 
						|
            if_exists: true,
 | 
						|
            database: true,
 | 
						|
            database_alias: Ident::new("db_name"),
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_detach_database_simple() {
 | 
						|
    let sql = r#"DETACH db_name"#;
 | 
						|
    let stmt = duckdb().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::DetachDuckDBDatabase {
 | 
						|
            if_exists: false,
 | 
						|
            database: false,
 | 
						|
            database_alias: Ident::new("db_name"),
 | 
						|
        },
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_duckdb_named_argument_function_with_assignment_operator() {
 | 
						|
    let sql = "SELECT FUN(a := '1', b := '2') FROM foo";
 | 
						|
    let select = duckdb_and_generic().verified_only_select(sql);
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Function(Function {
 | 
						|
            name: ObjectName(vec![Ident::new("FUN")]),
 | 
						|
            parameters: FunctionArguments::None,
 | 
						|
            args: FunctionArguments::List(FunctionArgumentList {
 | 
						|
                duplicate_treatment: None,
 | 
						|
                args: vec![
 | 
						|
                    FunctionArg::Named {
 | 
						|
                        name: Ident::new("a"),
 | 
						|
                        arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
 | 
						|
                            "1".to_owned()
 | 
						|
                        ))),
 | 
						|
                        operator: FunctionArgOperator::Assignment
 | 
						|
                    },
 | 
						|
                    FunctionArg::Named {
 | 
						|
                        name: Ident::new("b"),
 | 
						|
                        arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
 | 
						|
                            "2".to_owned()
 | 
						|
                        ))),
 | 
						|
                        operator: FunctionArgOperator::Assignment
 | 
						|
                    },
 | 
						|
                ],
 | 
						|
                clauses: vec![],
 | 
						|
            }),
 | 
						|
            null_treatment: None,
 | 
						|
            filter: None,
 | 
						|
            over: None,
 | 
						|
            within_group: vec![],
 | 
						|
        }),
 | 
						|
        expr_from_projection(only(&select.projection))
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_array_index() {
 | 
						|
    let sql = r#"SELECT ['a', 'b', 'c'][3] AS three"#;
 | 
						|
    let select = duckdb().verified_only_select(sql);
 | 
						|
    let projection = &select.projection;
 | 
						|
    assert_eq!(1, projection.len());
 | 
						|
    let expr = match &projection[0] {
 | 
						|
        SelectItem::ExprWithAlias { expr, .. } => expr,
 | 
						|
        _ => panic!("Expected an expression with alias"),
 | 
						|
    };
 | 
						|
    assert_eq!(
 | 
						|
        &Expr::Subscript {
 | 
						|
            expr: Box::new(Expr::Array(Array {
 | 
						|
                elem: vec![
 | 
						|
                    Expr::Value(Value::SingleQuotedString("a".to_owned())),
 | 
						|
                    Expr::Value(Value::SingleQuotedString("b".to_owned())),
 | 
						|
                    Expr::Value(Value::SingleQuotedString("c".to_owned()))
 | 
						|
                ],
 | 
						|
                named: false
 | 
						|
            })),
 | 
						|
            subscript: Box::new(Subscript::Index {
 | 
						|
                index: Expr::Value(number("3"))
 | 
						|
            })
 | 
						|
        },
 | 
						|
        expr
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn test_duckdb_union_datatype() {
 | 
						|
    let sql = "CREATE TABLE tbl1 (one UNION(a INT), two UNION(a INT, b INT), nested UNION(a UNION(b INT)))";
 | 
						|
    let stmt = duckdb_and_generic().verified_stmt(sql);
 | 
						|
    assert_eq!(
 | 
						|
        Statement::CreateTable(CreateTable {
 | 
						|
            or_replace: Default::default(),
 | 
						|
            temporary: Default::default(),
 | 
						|
            external: Default::default(),
 | 
						|
            global: Default::default(),
 | 
						|
            if_not_exists: Default::default(),
 | 
						|
            transient: Default::default(),
 | 
						|
            volatile: Default::default(),
 | 
						|
            name: ObjectName(vec!["tbl1".into()]),
 | 
						|
            columns: vec![
 | 
						|
                ColumnDef {
 | 
						|
                    name: "one".into(),
 | 
						|
                    data_type: DataType::Union(vec![UnionField {
 | 
						|
                        field_name: "a".into(),
 | 
						|
                        field_type: DataType::Int(None)
 | 
						|
                    }]),
 | 
						|
                    collation: Default::default(),
 | 
						|
                    options: Default::default()
 | 
						|
                },
 | 
						|
                ColumnDef {
 | 
						|
                    name: "two".into(),
 | 
						|
                    data_type: DataType::Union(vec![
 | 
						|
                        UnionField {
 | 
						|
                            field_name: "a".into(),
 | 
						|
                            field_type: DataType::Int(None)
 | 
						|
                        },
 | 
						|
                        UnionField {
 | 
						|
                            field_name: "b".into(),
 | 
						|
                            field_type: DataType::Int(None)
 | 
						|
                        }
 | 
						|
                    ]),
 | 
						|
                    collation: Default::default(),
 | 
						|
                    options: Default::default()
 | 
						|
                },
 | 
						|
                ColumnDef {
 | 
						|
                    name: "nested".into(),
 | 
						|
                    data_type: DataType::Union(vec![UnionField {
 | 
						|
                        field_name: "a".into(),
 | 
						|
                        field_type: DataType::Union(vec![UnionField {
 | 
						|
                            field_name: "b".into(),
 | 
						|
                            field_type: DataType::Int(None)
 | 
						|
                        }])
 | 
						|
                    }]),
 | 
						|
                    collation: Default::default(),
 | 
						|
                    options: Default::default()
 | 
						|
                }
 | 
						|
            ],
 | 
						|
            constraints: Default::default(),
 | 
						|
            hive_distribution: HiveDistributionStyle::NONE,
 | 
						|
            hive_formats: Some(HiveFormat {
 | 
						|
                row_format: Default::default(),
 | 
						|
                serde_properties: Default::default(),
 | 
						|
                storage: Default::default(),
 | 
						|
                location: Default::default()
 | 
						|
            }),
 | 
						|
            table_properties: Default::default(),
 | 
						|
            with_options: Default::default(),
 | 
						|
            file_format: Default::default(),
 | 
						|
            location: Default::default(),
 | 
						|
            query: Default::default(),
 | 
						|
            without_rowid: Default::default(),
 | 
						|
            like: Default::default(),
 | 
						|
            clone: Default::default(),
 | 
						|
            engine: Default::default(),
 | 
						|
            comment: Default::default(),
 | 
						|
            auto_increment_offset: Default::default(),
 | 
						|
            default_charset: Default::default(),
 | 
						|
            collation: Default::default(),
 | 
						|
            on_commit: Default::default(),
 | 
						|
            on_cluster: Default::default(),
 | 
						|
            primary_key: Default::default(),
 | 
						|
            order_by: Default::default(),
 | 
						|
            partition_by: Default::default(),
 | 
						|
            cluster_by: Default::default(),
 | 
						|
            clustered_by: Default::default(),
 | 
						|
            options: Default::default(),
 | 
						|
            strict: Default::default(),
 | 
						|
            copy_grants: Default::default(),
 | 
						|
            enable_schema_evolution: Default::default(),
 | 
						|
            change_tracking: Default::default(),
 | 
						|
            data_retention_time_in_days: Default::default(),
 | 
						|
            max_data_extension_time_in_days: Default::default(),
 | 
						|
            default_ddl_collation: Default::default(),
 | 
						|
            with_aggregation_policy: Default::default(),
 | 
						|
            with_row_access_policy: Default::default(),
 | 
						|
            with_tags: Default::default()
 | 
						|
        }),
 | 
						|
        stmt
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn parse_use() {
 | 
						|
    let valid_object_names = [
 | 
						|
        "mydb",
 | 
						|
        "SCHEMA",
 | 
						|
        "DATABASE",
 | 
						|
        "CATALOG",
 | 
						|
        "WAREHOUSE",
 | 
						|
        "DEFAULT",
 | 
						|
    ];
 | 
						|
    let quote_styles = ['"', '\''];
 | 
						|
 | 
						|
    for object_name in &valid_object_names {
 | 
						|
        // Test single identifier without quotes
 | 
						|
        assert_eq!(
 | 
						|
            duckdb().verified_stmt(&format!("USE {}", object_name)),
 | 
						|
            Statement::Use(Use::Object(ObjectName(vec![Ident::new(
 | 
						|
                object_name.to_string()
 | 
						|
            )])))
 | 
						|
        );
 | 
						|
        for "e in "e_styles {
 | 
						|
            // Test single identifier with different type of quotes
 | 
						|
            assert_eq!(
 | 
						|
                duckdb().verified_stmt(&format!("USE {0}{1}{0}", quote, object_name)),
 | 
						|
                Statement::Use(Use::Object(ObjectName(vec![Ident::with_quote(
 | 
						|
                    quote,
 | 
						|
                    object_name.to_string(),
 | 
						|
                )])))
 | 
						|
            );
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for "e in "e_styles {
 | 
						|
        // Test double identifier with different type of quotes
 | 
						|
        assert_eq!(
 | 
						|
            duckdb().verified_stmt(&format!("USE {0}CATALOG{0}.{0}my_schema{0}", quote)),
 | 
						|
            Statement::Use(Use::Object(ObjectName(vec![
 | 
						|
                Ident::with_quote(quote, "CATALOG"),
 | 
						|
                Ident::with_quote(quote, "my_schema")
 | 
						|
            ])))
 | 
						|
        );
 | 
						|
    }
 | 
						|
    // Test double identifier without quotes
 | 
						|
    assert_eq!(
 | 
						|
        duckdb().verified_stmt("USE mydb.my_schema"),
 | 
						|
        Statement::Use(Use::Object(ObjectName(vec![
 | 
						|
            Ident::new("mydb"),
 | 
						|
            Ident::new("my_schema")
 | 
						|
        ])))
 | 
						|
    );
 | 
						|
}
 |