// 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 sqlparser::ast; use std::ops::Deref; use sqlparser::ast::*; use sqlparser::dialect::{BigQueryDialect, GenericDialect}; use sqlparser::parser::ParserError; use test_utils::*; #[test] fn parse_literal_string() { let sql = r#"SELECT 'single', "double""#; let select = bigquery().verified_only_select(sql); assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Value(Value::SingleQuotedString("single".to_string())), expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Value(Value::DoubleQuotedString("double".to_string())), expr_from_projection(&select.projection[1]) ); } #[test] fn parse_byte_literal() { let sql = r#"SELECT B'abc', B"abc""#; let select = bigquery().verified_only_select(sql); assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Value(Value::SingleQuotedByteStringLiteral("abc".to_string())), expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Value(Value::DoubleQuotedByteStringLiteral("abc".to_string())), expr_from_projection(&select.projection[1]) ); let sql = r#"SELECT b'abc', b"abc""#; bigquery().one_statement_parses_to(sql, r#"SELECT B'abc', B"abc""#); } #[test] fn parse_raw_literal() { let sql = r#"SELECT R'abc', R"abc", R'f\(abc,(.*),def\)', R"f\(abc,(.*),def\)""#; let stmt = bigquery().one_statement_parses_to( sql, r"SELECT R'abc', R'abc', R'f\(abc,(.*),def\)', R'f\(abc,(.*),def\)'", ); if let Statement::Query(query) = stmt { if let SetExpr::Select(select) = *query.body { assert_eq!(4, select.projection.len()); assert_eq!( &Expr::Value(Value::RawStringLiteral("abc".to_string())), expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Value(Value::RawStringLiteral("abc".to_string())), expr_from_projection(&select.projection[1]) ); assert_eq!( &Expr::Value(Value::RawStringLiteral(r"f\(abc,(.*),def\)".to_string())), expr_from_projection(&select.projection[2]) ); assert_eq!( &Expr::Value(Value::RawStringLiteral(r"f\(abc,(.*),def\)".to_string())), expr_from_projection(&select.projection[3]) ); return; } } panic!("invalid query") } #[test] fn parse_nested_data_types() { let sql = "CREATE TABLE table (x STRUCT, b BYTES(42)>, y ARRAY>)"; match bigquery().one_statement_parses_to(sql, sql) { Statement::CreateTable { name, columns, .. } => { assert_eq!(name, ObjectName(vec!["table".into()])); assert_eq!( columns, vec![ ColumnDef { name: Ident::new("x"), data_type: DataType::Struct(vec![ StructField { field_name: Some("a".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket( Box::new(DataType::Int64,) )) }, StructField { field_name: Some("b".into()), field_type: DataType::Bytes(Some(42)) }, ]), collation: None, options: vec![], }, ColumnDef { name: Ident::new("y"), data_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Struct(vec![StructField { field_name: None, field_type: DataType::Int64, }]), ))), collation: None, options: vec![], }, ] ); } _ => unreachable!(), } } #[test] fn parse_invalid_brackets() { let sql = "SELECT STRUCT>(NULL)"; assert_eq!( bigquery().parse_sql_statements(sql).unwrap_err(), ParserError::ParserError("unmatched > in STRUCT literal".to_string()) ); let sql = "SELECT STRUCT>>(NULL)"; assert_eq!( bigquery().parse_sql_statements(sql).unwrap_err(), ParserError::ParserError("Expected (, found: >".to_string()) ); let sql = "CREATE TABLE table (x STRUCT>>)"; assert_eq!( bigquery().parse_sql_statements(sql).unwrap_err(), ParserError::ParserError( "Expected ',' or ')' after column definition, found: >".to_string() ) ); } #[test] fn parse_tuple_struct_literal() { // tuple syntax: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#tuple_syntax // syntax: (expr1, expr2 [, ... ]) let sql = "SELECT (1, 2, 3), (1, 1.0, '123', true)"; let select = bigquery().verified_only_select(sql); assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Tuple(vec![ Expr::Value(number("1")), Expr::Value(number("2")), Expr::Value(number("3")), ]), expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Tuple(vec![ Expr::Value(number("1")), Expr::Value(number("1.0")), Expr::Value(Value::SingleQuotedString("123".to_string())), Expr::Value(Value::Boolean(true)) ]), expr_from_projection(&select.projection[1]) ); } #[test] fn parse_typeless_struct_syntax() { // typeless struct syntax https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#typeless_struct_syntax // syntax: STRUCT( expr1 [AS field_name] [, ... ]) let sql = "SELECT STRUCT(1, 2, 3), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1 AS a, 'abc' AS b), STRUCT(str_col AS abc)"; let select = bigquery().verified_only_select(sql); assert_eq!(5, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![ Expr::Value(number("1")), Expr::Value(number("2")), Expr::Value(number("3")), ], fields: Default::default() }, expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Value(Value::SingleQuotedString("abc".to_string())),], fields: Default::default() }, expr_from_projection(&select.projection[1]) ); assert_eq!( &Expr::Struct { values: vec![ Expr::Value(number("1")), Expr::CompoundIdentifier(vec![Ident::from("t"), Ident::from("str_col")]), ], fields: Default::default() }, expr_from_projection(&select.projection[2]) ); assert_eq!( &Expr::Struct { values: vec![ Expr::Named { expr: Expr::Value(number("1")).into(), name: Ident::from("a") }, Expr::Named { expr: Expr::Value(Value::SingleQuotedString("abc".to_string())).into(), name: Ident::from("b") }, ], fields: Default::default() }, expr_from_projection(&select.projection[3]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Named { expr: Expr::Identifier(Ident::from("str_col")).into(), name: Ident::from("abc") }], fields: Default::default() }, expr_from_projection(&select.projection[4]) ); } #[test] fn parse_typed_struct_syntax() { // typed struct syntax https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#typed_struct_syntax // syntax: STRUCT<[field_name] field_type, ...>( expr1 [, ... ]) let sql = r#"SELECT STRUCT(5), STRUCT(1, t.str_col), STRUCT, str STRUCT>(nested_col)"#; let select = bigquery().verified_only_select(sql); assert_eq!(3, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![Expr::Value(number("5")),], fields: vec![StructField { field_name: None, field_type: DataType::Int64, }] }, expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Struct { values: vec![ Expr::Value(number("1")), Expr::CompoundIdentifier(vec![ Ident { value: "t".into(), quote_style: None, }, Ident { value: "str_col".into(), quote_style: None, }, ]), ], fields: vec![ StructField { field_name: Some(Ident { value: "x".into(), quote_style: None, }), field_type: DataType::Int64 }, StructField { field_name: Some(Ident { value: "y".into(), quote_style: None, }), field_type: DataType::String(None) }, ] }, expr_from_projection(&select.projection[1]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Identifier(Ident { value: "nested_col".into(), quote_style: None, }),], fields: vec![ StructField { field_name: Some("arr".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Float64 ))) }, StructField { field_name: Some("str".into()), field_type: DataType::Struct(vec![StructField { field_name: None, field_type: DataType::Bool }]) }, ] }, expr_from_projection(&select.projection[2]) ); let sql = r#"SELECT STRUCT>(nested_col)"#; let select = bigquery().verified_only_select(sql); assert_eq!(1, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![Expr::Identifier(Ident { value: "nested_col".into(), quote_style: None, }),], fields: vec![ StructField { field_name: Some("x".into()), field_type: DataType::Struct(Default::default()) }, StructField { field_name: Some("y".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Struct(Default::default()) ))) }, ] }, expr_from_projection(&select.projection[0]) ); let sql = r#"SELECT STRUCT(true), STRUCT(B'abc')"#; let select = bigquery().verified_only_select(sql); assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![Expr::Value(Value::Boolean(true)),], fields: vec![StructField { field_name: None, field_type: DataType::Bool }] }, expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Value(Value::SingleQuotedByteStringLiteral( "abc".into() )),], fields: vec![StructField { field_name: None, field_type: DataType::Bytes(Some(42)) }] }, expr_from_projection(&select.projection[1]) ); let sql = r#"SELECT STRUCT("2011-05-05"), STRUCT(DATETIME '1999-01-01 01:23:34.45'), STRUCT(5.0), STRUCT(1)"#; let select = bigquery().verified_only_select(sql); assert_eq!(4, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![Expr::Value(Value::DoubleQuotedString( "2011-05-05".to_string() )),], fields: vec![StructField { field_name: None, field_type: DataType::Date }] }, expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Struct { values: vec![Expr::TypedString { data_type: DataType::Datetime(None), value: "1999-01-01 01:23:34.45".to_string() },], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None) }] }, expr_from_projection(&select.projection[1]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Value(number("5.0")),], fields: vec![StructField { field_name: None, field_type: DataType::Float64 }] }, expr_from_projection(&select.projection[2]) ); assert_eq!( &Expr::Struct { values: vec![Expr::Value(number("1")),], fields: vec![StructField { field_name: None, field_type: DataType::Int64 }] }, expr_from_projection(&select.projection[3]) ); let sql = r#"SELECT STRUCT(INTERVAL '1-2 3 4:5:6.789999'), STRUCT(JSON '{"class" : {"students" : [{"name" : "Jane"}]}}')"#; let select = bigquery().verified_only_select(sql); assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { values: vec![Expr::Interval(ast::Interval { value: Box::new(Expr::Value(Value::SingleQuotedString( "1-2 3 4:5:6.789999".to_string() ))), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None }),], fields: vec![StructField { field_name: None, field_type: DataType::Interval }] }, expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Struct { values: vec![Expr::TypedString { data_type: DataType::JSON, value: r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.to_string() },], fields: vec![StructField { field_name: None, field_type: DataType::JSON }] }, expr_from_projection(&select.projection[1]) ); let sql = r#"SELECT STRUCT("foo"), STRUCT(TIMESTAMP '2008-12-25 15:30:00 America/Los_Angeles'), STRUCT