From 062d25338964f3c7148785f62c8c990a8b7c03f1 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Tue, 19 Nov 2024 21:10:44 +0100 Subject: [PATCH 1/4] PartiQl ongoing --- src/ast/query.rs | 6 +++ src/dialect/mod.rs | 6 +++ src/dialect/redshift.rs | 5 ++ src/parser/mod.rs | 23 ++++++--- src/test_utils.rs | 2 + tests/sqlparser_bigquery.rs | 5 ++ tests/sqlparser_clickhouse.rs | 2 + tests/sqlparser_common.rs | 37 ++++++++++++++ tests/sqlparser_databricks.rs | 1 + tests/sqlparser_duckdb.rs | 2 + tests/sqlparser_hive.rs | 1 + tests/sqlparser_mssql.rs | 18 +++++-- tests/sqlparser_mysql.rs | 5 ++ tests/sqlparser_postgres.rs | 1 + tests/sqlparser_redshift.rs | 92 +++++++++++++++++++++++++++++++++++ tests/sqlparser_snowflake.rs | 1 + tests/sqlparser_sqlite.rs | 1 + 17 files changed, 197 insertions(+), 11 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 078bbc841..f0f9511e7 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -974,6 +974,8 @@ pub enum TableFactor { with_ordinality: bool, /// [Partition selection](https://dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html), supported by MySQL. partitions: Vec, + /// Optional PartiQL JsonPath: + partiql: Option, }, Derived { lateral: bool, @@ -1375,8 +1377,12 @@ impl fmt::Display for TableFactor { version, partitions, with_ordinality, + partiql, } => { write!(f, "{name}")?; + if let Some(partiql) = partiql { + write!(f, "{partiql}")?; + } if !partitions.is_empty() { write!(f, "PARTITION ({})", display_comma_separated(partitions))?; } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 985cad749..159e14717 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -675,6 +675,12 @@ pub trait Dialect: Debug + Any { fn supports_create_table_select(&self) -> bool { false } + + /// Returns true if the dialect supports PartiQL for querying semi-structured data + /// + fn supports_partiql(&self) -> bool { + false + } } /// This represents the operators for which precedence must be defined diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index 4d0773843..48eb00ab1 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -74,4 +74,9 @@ impl Dialect for RedshiftSqlDialect { fn supports_top_before_distinct(&self) -> bool { true } + + /// Redshift supports PartiQL: + fn supports_partiql(&self) -> bool { + true + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 35c763e93..eedef1d5a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2936,7 +2936,7 @@ impl<'a> Parser<'a> { } else if Token::LBracket == tok { if dialect_of!(self is PostgreSqlDialect | DuckDbDialect | GenericDialect) { self.parse_subscript(expr) - } else if dialect_of!(self is SnowflakeDialect) { + } else if dialect_of!(self is SnowflakeDialect) || self.dialect.supports_partiql() { self.prev_token(); self.parse_json_access(expr) } else { @@ -3072,6 +3072,14 @@ impl<'a> Parser<'a> { } fn parse_json_access(&mut self, expr: Expr) -> Result { + let path = self.parse_json_path()?; + Ok(Expr::JsonAccess { + value: Box::new(expr), + path, + }) + } + + fn parse_json_path(&mut self) -> Result { let mut path = Vec::new(); loop { match self.next_token().token { @@ -3095,10 +3103,7 @@ impl<'a> Parser<'a> { } debug_assert!(!path.is_empty()); - Ok(Expr::JsonAccess { - value: Box::new(expr), - path: JsonPath { path }, - }) + Ok(JsonPath{ path }) } pub fn parse_map_access(&mut self, expr: Expr) -> Result { @@ -10322,7 +10327,12 @@ impl<'a> Parser<'a> { self.parse_open_json_table_factor() } else { let name = self.parse_object_name(true)?; - + + let partiql = match self.peek_token().token { + Token::LBracket if self.dialect.supports_partiql() => Some(self.parse_json_path()?), + _ => None + }; + let partitions: Vec = if dialect_of!(self is MySqlDialect | GenericDialect) && self.parse_keyword(Keyword::PARTITION) { @@ -10365,6 +10375,7 @@ impl<'a> Parser<'a> { version, partitions, with_ordinality, + partiql, }; while let Some(kw) = self.parse_one_of_keywords(&[Keyword::PIVOT, Keyword::UNPIVOT]) { diff --git a/src/test_utils.rs b/src/test_utils.rs index b35fc45c2..b0a0b2093 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -345,6 +345,7 @@ pub fn table(name: impl Into) -> TableFactor { version: None, partitions: vec![], with_ordinality: false, + partiql: None, } } @@ -360,6 +361,7 @@ pub fn table_with_alias(name: impl Into, alias: impl Into) -> Ta version: None, partitions: vec![], with_ordinality: false, + partiql: None, } } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 2bf470f71..a4e0f0706 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -229,6 +229,7 @@ fn parse_delete_statement() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation ); @@ -1373,6 +1374,7 @@ fn parse_table_identifiers() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] },] @@ -1546,6 +1548,7 @@ fn parse_table_time_travel() { ))), partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] },] @@ -1644,6 +1647,7 @@ fn parse_merge() { version: Default::default(), partitions: Default::default(), with_ordinality: false, + partiql: None, }, table ); @@ -1659,6 +1663,7 @@ fn parse_merge() { version: Default::default(), partitions: Default::default(), with_ordinality: false, + partiql: None, }, source ); diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index a71871115..d6d753ebb 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -67,6 +67,7 @@ fn parse_map_access_expr() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -172,6 +173,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 3d9ba5da2..5139dd722 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -364,6 +364,7 @@ fn parse_update_set_from() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }, @@ -394,6 +395,7 @@ fn parse_update_set_from() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -473,6 +475,7 @@ fn parse_update_with_table_alias() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }, @@ -564,6 +567,7 @@ fn parse_select_with_table_alias() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }] @@ -601,6 +605,7 @@ fn parse_delete_statement() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation ); @@ -648,6 +653,7 @@ fn parse_delete_statement_for_multi_tables() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation ); @@ -660,6 +666,7 @@ fn parse_delete_statement_for_multi_tables() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].joins[0].relation ); @@ -686,6 +693,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation ); @@ -698,6 +706,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[1].relation ); @@ -710,6 +719,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, using[0].relation ); @@ -722,6 +732,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, using[0].joins[0].relation ); @@ -753,6 +764,7 @@ fn parse_where_delete_statement() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation, ); @@ -798,6 +810,7 @@ fn parse_where_delete_with_alias_statement() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, from[0].relation, ); @@ -814,6 +827,7 @@ fn parse_where_delete_with_alias_statement() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }]), @@ -4718,6 +4732,7 @@ fn test_parse_named_window() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -5301,6 +5316,7 @@ fn parse_interval_and_or_xor() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -5912,6 +5928,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }, @@ -5924,6 +5941,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }, @@ -5944,6 +5962,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -5954,6 +5973,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -5968,6 +5988,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -5978,6 +5999,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -6002,6 +6024,7 @@ fn parse_cross_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: JoinOperator::CrossJoin, @@ -6027,6 +6050,7 @@ fn parse_joins_on() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global, join_operator: f(JoinConstraint::On(Expr::BinaryOp { @@ -6154,6 +6178,7 @@ fn parse_joins_using() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: f(JoinConstraint::Using(vec!["c1".into()])), @@ -6227,6 +6252,7 @@ fn parse_natural_join() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: f(JoinConstraint::Natural), @@ -6496,6 +6522,7 @@ fn parse_derived_tables() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -7443,6 +7470,7 @@ fn lateral_function() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![Join { relation: TableFactor::Function { @@ -8258,6 +8286,7 @@ fn parse_merge() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, } ); assert_eq!(table, table_no_into); @@ -8285,6 +8314,7 @@ fn parse_merge() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -9359,6 +9389,7 @@ fn parse_pivot_table() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }), aggregate_functions: vec![ expected_function("a", None), @@ -9432,6 +9463,7 @@ fn parse_unpivot_table() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }), value: Ident { value: "quantity".to_string(), @@ -9499,6 +9531,7 @@ fn parse_pivot_unpivot_table() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }), value: Ident { value: "population".to_string(), @@ -9910,6 +9943,7 @@ fn parse_unload() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -10089,6 +10123,7 @@ fn parse_connect_by() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -10176,6 +10211,7 @@ fn parse_connect_by() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -10337,6 +10373,7 @@ fn test_match_recognize() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }; fn check(options: &str, expect: TableFactor) { diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 7b917bd06..e8806bc0f 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -193,6 +193,7 @@ fn test_values_clause() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }), query .body diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index a2db5c282..899dbfdfc 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -282,6 +282,7 @@ fn test_select_union_by_name() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], @@ -323,6 +324,7 @@ fn test_select_union_by_name() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }], diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 10bd374c0..0d39e2920 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -457,6 +457,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 73fd99cf3..74443d77e 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -70,6 +70,7 @@ fn parse_table_time_travel() { ))), partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] },] @@ -218,7 +219,8 @@ fn parse_mssql_openjson() { with_hints: vec![], version: None, with_ordinality: false, - partitions: vec![] + partitions: vec![], + partiql: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -293,7 +295,8 @@ fn parse_mssql_openjson() { with_hints: vec![], version: None, with_ordinality: false, - partitions: vec![] + partitions: vec![], + partiql: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -368,7 +371,8 @@ fn parse_mssql_openjson() { with_hints: vec![], version: None, with_ordinality: false, - partitions: vec![] + partitions: vec![], + partiql: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -443,7 +447,8 @@ fn parse_mssql_openjson() { with_hints: vec![], version: None, with_ordinality: false, - partitions: vec![] + partitions: vec![], + partiql: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -496,7 +501,8 @@ fn parse_mssql_openjson() { with_hints: vec![], version: None, with_ordinality: false, - partitions: vec![] + partitions: vec![], + partiql: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -679,6 +685,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); @@ -1314,6 +1321,7 @@ fn parse_substring_in_select() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] }], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index ce3296737..d0499ad32 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1862,6 +1862,7 @@ fn parse_select_with_numeric_prefix_column_name() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] }], @@ -1918,6 +1919,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] }], @@ -1985,6 +1987,7 @@ fn parse_update_with_joins() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -1998,6 +2001,7 @@ fn parse_update_with_joins() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { @@ -2428,6 +2432,7 @@ fn parse_substring_in_select() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![] }], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 2e2c4403c..4cd7eedd1 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -3511,6 +3511,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index a25d50605..2c65fe8fc 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -54,6 +54,7 @@ fn test_square_brackets_over_db_schema_table_name() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], } @@ -101,6 +102,7 @@ fn test_double_quotes_over_db_schema_table_name() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], } @@ -123,6 +125,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); @@ -196,3 +199,92 @@ fn test_create_view_with_no_schema_binding() { redshift_and_generic() .verified_stmt("CREATE VIEW myevent AS SELECT eventname FROM event WITH NO SCHEMA BINDING"); } + +#[test] +fn test_redshift_json_path() { + let sql = "SELECT cust.c_orders[0].o_orderkey FROM customer_orders_lineitem"; + let select = redshift().verified_only_select(sql); + + assert_eq!( + &Expr::JsonAccess{ + value: Box::new(Expr::CompoundIdentifier(vec![ + Ident::new("cust"), + Ident::new("c_orders") + ])), + path: JsonPath{ + path: vec![ + JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, + JsonPathElem::Dot{key: "o_orderkey".to_string(), quoted: false} + ] + } + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT cust.c_orders[0]['id'] FROM customer_orders_lineitem"; + let select = redshift().verified_only_select(sql); + assert_eq!( + &Expr::JsonAccess{ + value: Box::new(Expr::CompoundIdentifier(vec![ + Ident::new("cust"), + Ident::new("c_orders") + ])), + path: JsonPath{ + path: vec![ + JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, + JsonPathElem::Bracket{key: Expr::Value(Value::SingleQuotedString("id".to_owned()))} + ] + } + }, + expr_from_projection(only(&select.projection)) + ); +} + +#[test] +fn test_parse_json_path_from() { + let select = redshift().verified_only_select("SELECT * FROM src[0].a AS a"); + match &select.from[0].relation { + TableFactor::Table { name, partiql, .. } => { + assert_eq!(name, &ObjectName(vec![Ident::new("src")])); + assert_eq!( + partiql, + &Some(JsonPath{ + path: vec![ + JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, + JsonPathElem::Dot{key: "a".to_string(), quoted: false} + ] + }) + ); + } + _ => panic!(), + } + + let select = redshift().verified_only_select("SELECT * FROM src[0].a[1].b AS a"); + match &select.from[0].relation { + TableFactor::Table { name, partiql, .. } => { + assert_eq!(name, &ObjectName(vec![Ident::new("src")])); + assert_eq!( + partiql, + &Some(JsonPath{ + path: vec![ + JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, + JsonPathElem::Dot{key: "a".to_string(), quoted: false}, + JsonPathElem::Bracket{key: Expr::Value(Value::Number("1".to_string(), false))}, + JsonPathElem::Dot{key: "b".to_string(), quoted: false}, + ] + }) + ); + } + _ => panic!(), + } + + let select = redshift().verified_only_select("SELECT * FROM src.a.b"); + match &select.from[0].relation { + TableFactor::Table { name, partiql, .. } => { + assert_eq!(name, &ObjectName(vec![Ident::new("src"), Ident::new("a"), Ident::new("b")])); + assert_eq!(partiql, &None); + } + _ => panic!(), + } + +} \ No newline at end of file diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 1f1c00e7a..ef2726d3b 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1190,6 +1190,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, + partiql: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 6f8e654dc..3d4a2c614 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -486,6 +486,7 @@ fn parse_update_tuple_row_values() { version: None, partitions: vec![], with_ordinality: false, + partiql: None, }, joins: vec![], }, From 255907550e25ce09909d327053eb79472c63f5a6 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Tue, 19 Nov 2024 21:33:50 +0100 Subject: [PATCH 2/4] Add support for PartiQL queries in Redshift --- src/parser/mod.rs | 8 ++--- tests/sqlparser_redshift.rs | 64 ++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index eedef1d5a..200e8248b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3103,7 +3103,7 @@ impl<'a> Parser<'a> { } debug_assert!(!path.is_empty()); - Ok(JsonPath{ path }) + Ok(JsonPath { path }) } pub fn parse_map_access(&mut self, expr: Expr) -> Result { @@ -10327,12 +10327,12 @@ impl<'a> Parser<'a> { self.parse_open_json_table_factor() } else { let name = self.parse_object_name(true)?; - + let partiql = match self.peek_token().token { Token::LBracket if self.dialect.supports_partiql() => Some(self.parse_json_path()?), - _ => None + _ => None, }; - + let partitions: Vec = if dialect_of!(self is MySqlDialect | GenericDialect) && self.parse_keyword(Keyword::PARTITION) { diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 2c65fe8fc..ff378071c 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -206,15 +206,20 @@ fn test_redshift_json_path() { let select = redshift().verified_only_select(sql); assert_eq!( - &Expr::JsonAccess{ + &Expr::JsonAccess { value: Box::new(Expr::CompoundIdentifier(vec![ Ident::new("cust"), Ident::new("c_orders") ])), - path: JsonPath{ + path: JsonPath { path: vec![ - JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, - JsonPathElem::Dot{key: "o_orderkey".to_string(), quoted: false} + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("0".parse().unwrap(), false)) + }, + JsonPathElem::Dot { + key: "o_orderkey".to_string(), + quoted: false + } ] } }, @@ -224,15 +229,19 @@ fn test_redshift_json_path() { let sql = "SELECT cust.c_orders[0]['id'] FROM customer_orders_lineitem"; let select = redshift().verified_only_select(sql); assert_eq!( - &Expr::JsonAccess{ + &Expr::JsonAccess { value: Box::new(Expr::CompoundIdentifier(vec![ Ident::new("cust"), Ident::new("c_orders") ])), - path: JsonPath{ + path: JsonPath { path: vec![ - JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, - JsonPathElem::Bracket{key: Expr::Value(Value::SingleQuotedString("id".to_owned()))} + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("0".parse().unwrap(), false)) + }, + JsonPathElem::Bracket { + key: Expr::Value(Value::SingleQuotedString("id".to_owned())) + } ] } }, @@ -248,10 +257,15 @@ fn test_parse_json_path_from() { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( partiql, - &Some(JsonPath{ + &Some(JsonPath { path: vec![ - JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, - JsonPathElem::Dot{key: "a".to_string(), quoted: false} + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("0".parse().unwrap(), false)) + }, + JsonPathElem::Dot { + key: "a".to_string(), + quoted: false + } ] }) ); @@ -265,12 +279,22 @@ fn test_parse_json_path_from() { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( partiql, - &Some(JsonPath{ + &Some(JsonPath { path: vec![ - JsonPathElem::Bracket{key: Expr::Value(Value::Number("0".to_string(), false))}, - JsonPathElem::Dot{key: "a".to_string(), quoted: false}, - JsonPathElem::Bracket{key: Expr::Value(Value::Number("1".to_string(), false))}, - JsonPathElem::Dot{key: "b".to_string(), quoted: false}, + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("0".parse().unwrap(), false)) + }, + JsonPathElem::Dot { + key: "a".to_string(), + quoted: false + }, + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("1".parse().unwrap(), false)) + }, + JsonPathElem::Dot { + key: "b".to_string(), + quoted: false + }, ] }) ); @@ -281,10 +305,12 @@ fn test_parse_json_path_from() { let select = redshift().verified_only_select("SELECT * FROM src.a.b"); match &select.from[0].relation { TableFactor::Table { name, partiql, .. } => { - assert_eq!(name, &ObjectName(vec![Ident::new("src"), Ident::new("a"), Ident::new("b")])); + assert_eq!( + name, + &ObjectName(vec![Ident::new("src"), Ident::new("a"), Ident::new("b")]) + ); assert_eq!(partiql, &None); } _ => panic!(), } - -} \ No newline at end of file +} From 08890d187f995dbbf50db426bf88525b7fbb8f45 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Thu, 21 Nov 2024 14:49:16 +0100 Subject: [PATCH 3/4] Code review comments --- src/ast/query.rs | 8 ++-- src/parser/mod.rs | 4 +- src/test_utils.rs | 4 +- tests/sqlparser_bigquery.rs | 10 ++--- tests/sqlparser_clickhouse.rs | 4 +- tests/sqlparser_common.rs | 74 +++++++++++++++++------------------ tests/sqlparser_databricks.rs | 2 +- tests/sqlparser_duckdb.rs | 4 +- tests/sqlparser_hive.rs | 2 +- tests/sqlparser_mssql.rs | 16 ++++---- tests/sqlparser_mysql.rs | 10 ++--- tests/sqlparser_postgres.rs | 2 +- tests/sqlparser_redshift.rs | 54 ++++++++++++++++++------- tests/sqlparser_snowflake.rs | 2 +- tests/sqlparser_sqlite.rs | 2 +- 15 files changed, 112 insertions(+), 86 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index f0f9511e7..bf36c626f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -975,7 +975,7 @@ pub enum TableFactor { /// [Partition selection](https://dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html), supported by MySQL. partitions: Vec, /// Optional PartiQL JsonPath: - partiql: Option, + json_path: Option, }, Derived { lateral: bool, @@ -1377,11 +1377,11 @@ impl fmt::Display for TableFactor { version, partitions, with_ordinality, - partiql, + json_path, } => { write!(f, "{name}")?; - if let Some(partiql) = partiql { - write!(f, "{partiql}")?; + if let Some(json_path) = json_path { + write!(f, "{json_path}")?; } if !partitions.is_empty() { write!(f, "PARTITION ({})", display_comma_separated(partitions))?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 200e8248b..1f749a88c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -10328,7 +10328,7 @@ impl<'a> Parser<'a> { } else { let name = self.parse_object_name(true)?; - let partiql = match self.peek_token().token { + let json_path = match self.peek_token().token { Token::LBracket if self.dialect.supports_partiql() => Some(self.parse_json_path()?), _ => None, }; @@ -10375,7 +10375,7 @@ impl<'a> Parser<'a> { version, partitions, with_ordinality, - partiql, + json_path, }; while let Some(kw) = self.parse_one_of_keywords(&[Keyword::PIVOT, Keyword::UNPIVOT]) { diff --git a/src/test_utils.rs b/src/test_utils.rs index b0a0b2093..aaee20c5f 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -345,7 +345,7 @@ pub fn table(name: impl Into) -> TableFactor { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, } } @@ -361,7 +361,7 @@ pub fn table_with_alias(name: impl Into, alias: impl Into) -> Ta version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, } } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index a4e0f0706..d4c178bbf 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -229,7 +229,7 @@ fn parse_delete_statement() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation ); @@ -1374,7 +1374,7 @@ fn parse_table_identifiers() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] },] @@ -1548,7 +1548,7 @@ fn parse_table_time_travel() { ))), partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] },] @@ -1647,7 +1647,7 @@ fn parse_merge() { version: Default::default(), partitions: Default::default(), with_ordinality: false, - partiql: None, + json_path: None, }, table ); @@ -1663,7 +1663,7 @@ fn parse_merge() { version: Default::default(), partitions: Default::default(), with_ordinality: false, - partiql: None, + json_path: None, }, source ); diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index d6d753ebb..90af12ab7 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -67,7 +67,7 @@ fn parse_map_access_expr() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -173,7 +173,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5139dd722..b41063859 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -364,7 +364,7 @@ fn parse_update_set_from() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }, @@ -395,7 +395,7 @@ fn parse_update_set_from() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -475,7 +475,7 @@ fn parse_update_with_table_alias() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }, @@ -567,7 +567,7 @@ fn parse_select_with_table_alias() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }] @@ -605,7 +605,7 @@ fn parse_delete_statement() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation ); @@ -653,7 +653,7 @@ fn parse_delete_statement_for_multi_tables() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation ); @@ -666,7 +666,7 @@ fn parse_delete_statement_for_multi_tables() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].joins[0].relation ); @@ -693,7 +693,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation ); @@ -706,7 +706,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[1].relation ); @@ -719,7 +719,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, using[0].relation ); @@ -732,7 +732,7 @@ fn parse_delete_statement_for_multi_tables_with_using() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, using[0].joins[0].relation ); @@ -764,7 +764,7 @@ fn parse_where_delete_statement() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation, ); @@ -810,7 +810,7 @@ fn parse_where_delete_with_alias_statement() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, from[0].relation, ); @@ -827,7 +827,7 @@ fn parse_where_delete_with_alias_statement() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }]), @@ -4732,7 +4732,7 @@ fn test_parse_named_window() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -5316,7 +5316,7 @@ fn parse_interval_and_or_xor() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -5928,7 +5928,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }, @@ -5941,7 +5941,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }, @@ -5962,7 +5962,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -5973,7 +5973,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -5988,7 +5988,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -5999,7 +5999,7 @@ fn parse_implicit_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -6024,7 +6024,7 @@ fn parse_cross_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: JoinOperator::CrossJoin, @@ -6050,7 +6050,7 @@ fn parse_joins_on() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global, join_operator: f(JoinConstraint::On(Expr::BinaryOp { @@ -6178,7 +6178,7 @@ fn parse_joins_using() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: f(JoinConstraint::Using(vec!["c1".into()])), @@ -6252,7 +6252,7 @@ fn parse_natural_join() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: f(JoinConstraint::Natural), @@ -6522,7 +6522,7 @@ fn parse_derived_tables() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -7470,7 +7470,7 @@ fn lateral_function() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::Function { @@ -8286,7 +8286,7 @@ fn parse_merge() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, } ); assert_eq!(table, table_no_into); @@ -8314,7 +8314,7 @@ fn parse_merge() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -9389,7 +9389,7 @@ fn parse_pivot_table() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }), aggregate_functions: vec![ expected_function("a", None), @@ -9463,7 +9463,7 @@ fn parse_unpivot_table() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }), value: Ident { value: "quantity".to_string(), @@ -9531,7 +9531,7 @@ fn parse_pivot_unpivot_table() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }), value: Ident { value: "population".to_string(), @@ -9943,7 +9943,7 @@ fn parse_unload() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -10123,7 +10123,7 @@ fn parse_connect_by() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -10211,7 +10211,7 @@ fn parse_connect_by() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -10373,7 +10373,7 @@ fn test_match_recognize() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }; fn check(options: &str, expect: TableFactor) { diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index e8806bc0f..1651d517a 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -193,7 +193,7 @@ fn test_values_clause() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }), query .body diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 899dbfdfc..73b0f6601 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -282,7 +282,7 @@ fn test_select_union_by_name() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], @@ -324,7 +324,7 @@ fn test_select_union_by_name() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }], diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 0d39e2920..8d4f7a680 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -457,7 +457,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 74443d77e..74f3c077e 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -70,7 +70,7 @@ fn parse_table_time_travel() { ))), partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] },] @@ -220,7 +220,7 @@ fn parse_mssql_openjson() { version: None, with_ordinality: false, partitions: vec![], - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -296,7 +296,7 @@ fn parse_mssql_openjson() { version: None, with_ordinality: false, partitions: vec![], - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -372,7 +372,7 @@ fn parse_mssql_openjson() { version: None, with_ordinality: false, partitions: vec![], - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -448,7 +448,7 @@ fn parse_mssql_openjson() { version: None, with_ordinality: false, partitions: vec![], - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -502,7 +502,7 @@ fn parse_mssql_openjson() { version: None, with_ordinality: false, partitions: vec![], - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::OpenJsonTable { @@ -685,7 +685,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); @@ -1321,7 +1321,7 @@ fn parse_substring_in_select() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] }], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index d0499ad32..3d8b08630 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1862,7 +1862,7 @@ fn parse_select_with_numeric_prefix_column_name() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] }], @@ -1919,7 +1919,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] }], @@ -1987,7 +1987,7 @@ fn parse_update_with_joins() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![Join { relation: TableFactor::Table { @@ -2001,7 +2001,7 @@ fn parse_update_with_joins() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, global: false, join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { @@ -2432,7 +2432,7 @@ fn parse_substring_in_select() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![] }], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 4cd7eedd1..098a3464c 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -3511,7 +3511,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index ff378071c..a99781cd6 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -54,7 +54,7 @@ fn test_square_brackets_over_db_schema_table_name() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], } @@ -102,7 +102,7 @@ fn test_double_quotes_over_db_schema_table_name() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], } @@ -125,7 +125,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); @@ -202,8 +202,9 @@ fn test_create_view_with_no_schema_binding() { #[test] fn test_redshift_json_path() { + let dialects = all_dialects_where(|d| d.supports_partiql()); let sql = "SELECT cust.c_orders[0].o_orderkey FROM customer_orders_lineitem"; - let select = redshift().verified_only_select(sql); + let select = dialects.verified_only_select(sql); assert_eq!( &Expr::JsonAccess { @@ -227,7 +228,7 @@ fn test_redshift_json_path() { ); let sql = "SELECT cust.c_orders[0]['id'] FROM customer_orders_lineitem"; - let select = redshift().verified_only_select(sql); + let select = dialects.verified_only_select(sql); assert_eq!( &Expr::JsonAccess { value: Box::new(Expr::CompoundIdentifier(vec![ @@ -247,16 +248,41 @@ fn test_redshift_json_path() { }, expr_from_projection(only(&select.projection)) ); + + let sql = "SELECT db1.sc1.tbl1.col1[0]['id'] FROM customer_orders_lineitem"; + let select = dialects.verified_only_select(sql); + assert_eq!( + &Expr::JsonAccess { + value: Box::new(Expr::CompoundIdentifier(vec![ + Ident::new("db1"), + Ident::new("sc1"), + Ident::new("tbl1"), + Ident::new("col1") + ])), + path: JsonPath { + path: vec![ + JsonPathElem::Bracket { + key: Expr::Value(Value::Number("0".parse().unwrap(), false)) + }, + JsonPathElem::Bracket { + key: Expr::Value(Value::SingleQuotedString("id".to_owned())) + } + ] + } + }, + expr_from_projection(only(&select.projection)) + ); } #[test] fn test_parse_json_path_from() { - let select = redshift().verified_only_select("SELECT * FROM src[0].a AS a"); + let dialects = all_dialects_where(|d| d.supports_partiql()); + let select = dialects.verified_only_select("SELECT * FROM src[0].a AS a"); match &select.from[0].relation { - TableFactor::Table { name, partiql, .. } => { + TableFactor::Table { name, json_path, .. } => { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( - partiql, + json_path, &Some(JsonPath { path: vec![ JsonPathElem::Bracket { @@ -273,12 +299,12 @@ fn test_parse_json_path_from() { _ => panic!(), } - let select = redshift().verified_only_select("SELECT * FROM src[0].a[1].b AS a"); + let select = dialects.verified_only_select("SELECT * FROM src[0].a[1].b AS a"); match &select.from[0].relation { - TableFactor::Table { name, partiql, .. } => { + TableFactor::Table { name, json_path, .. } => { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( - partiql, + json_path, &Some(JsonPath { path: vec![ JsonPathElem::Bracket { @@ -302,14 +328,14 @@ fn test_parse_json_path_from() { _ => panic!(), } - let select = redshift().verified_only_select("SELECT * FROM src.a.b"); + let select = dialects.verified_only_select("SELECT * FROM src.a.b"); match &select.from[0].relation { - TableFactor::Table { name, partiql, .. } => { + TableFactor::Table { name, json_path, .. } => { assert_eq!( name, &ObjectName(vec![Ident::new("src"), Ident::new("a"), Ident::new("b")]) ); - assert_eq!(partiql, &None); + assert_eq!(json_path, &None); } _ => panic!(), } diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index ef2726d3b..930e94c0f 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1190,7 +1190,7 @@ fn parse_delimited_identifiers() { version, with_ordinality: _, partitions: _, - partiql: _, + json_path: _, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 3d4a2c614..c3cfb7a63 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -486,7 +486,7 @@ fn parse_update_tuple_row_values() { version: None, partitions: vec![], with_ordinality: false, - partiql: None, + json_path: None, }, joins: vec![], }, From f2bab074787b1434212d1bee05f21ef983946fa0 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Thu, 21 Nov 2024 14:51:30 +0100 Subject: [PATCH 4/4] Code review comments --- tests/sqlparser_redshift.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index a99781cd6..0a084b340 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -279,7 +279,9 @@ fn test_parse_json_path_from() { let dialects = all_dialects_where(|d| d.supports_partiql()); let select = dialects.verified_only_select("SELECT * FROM src[0].a AS a"); match &select.from[0].relation { - TableFactor::Table { name, json_path, .. } => { + TableFactor::Table { + name, json_path, .. + } => { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( json_path, @@ -301,7 +303,9 @@ fn test_parse_json_path_from() { let select = dialects.verified_only_select("SELECT * FROM src[0].a[1].b AS a"); match &select.from[0].relation { - TableFactor::Table { name, json_path, .. } => { + TableFactor::Table { + name, json_path, .. + } => { assert_eq!(name, &ObjectName(vec![Ident::new("src")])); assert_eq!( json_path, @@ -330,7 +334,9 @@ fn test_parse_json_path_from() { let select = dialects.verified_only_select("SELECT * FROM src.a.b"); match &select.from[0].relation { - TableFactor::Table { name, json_path, .. } => { + TableFactor::Table { + name, json_path, .. + } => { assert_eq!( name, &ObjectName(vec![Ident::new("src"), Ident::new("a"), Ident::new("b")])