Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,12 @@ pub enum Expr {
kind: CastKind,
expr: Box<Expr>,
data_type: DataType,
/// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB
/// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key
/// specifications, so it's a flag here.
///
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast
array: bool,
/// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
Expand Down Expand Up @@ -1724,14 +1730,18 @@ impl fmt::Display for Expr {
kind,
expr,
data_type,
array,
format,
} => match kind {
CastKind::Cast => {
write!(f, "CAST({expr} AS {data_type}")?;
if *array {
write!(f, " ARRAY")?;
}
if let Some(format) = format {
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
} else {
write!(f, "CAST({expr} AS {data_type})")
write!(f, " FORMAT {format}")?;
}
write!(f, ")")
}
CastKind::TryCast => {
if let Some(format) = format {
Expand Down
3 changes: 2 additions & 1 deletion src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,7 @@ impl Spanned for Expr {
kind: _,
expr,
data_type: _,
array: _,
format: _,
} => expr.span(),
Expr::AtTimeZone {
Expand Down Expand Up @@ -2800,7 +2801,7 @@ WHERE id = 1
UPDATE SET target_table.description = source_table.description

WHEN MATCHED AND target_table.x != 'X' THEN DELETE
WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW
WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW
"#;

let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap();
Expand Down
4 changes: 4 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2539,12 +2539,14 @@ impl<'a> Parser<'a> {
let expr = self.parse_expr()?;
self.expect_keyword_is(Keyword::AS)?;
let data_type = self.parse_data_type()?;
let array = self.parse_keyword(Keyword::ARRAY);
let format = self.parse_optional_cast_format()?;
self.expect_token(&Token::RParen)?;
Ok(Expr::Cast {
kind,
expr: Box::new(expr),
data_type,
array,
format,
})
}
Expand Down Expand Up @@ -3803,6 +3805,7 @@ impl<'a> Parser<'a> {
kind: CastKind::DoubleColon,
expr: Box::new(expr),
data_type: self.parse_data_type()?,
array: false,
format: None,
})
} else if Token::ExclamationMark == *tok && self.dialect.supports_factorial_operator() {
Expand Down Expand Up @@ -4041,6 +4044,7 @@ impl<'a> Parser<'a> {
kind: CastKind::DoubleColon,
expr: Box::new(expr),
data_type: self.parse_data_type()?,
array: false,
format: None,
})
}
Expand Down
18 changes: 18 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3004,6 +3004,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::BigInt(None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3016,6 +3017,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::TinyInt(None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand Down Expand Up @@ -3047,6 +3049,7 @@ fn parse_cast() {
length: 50,
unit: None,
})),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3059,6 +3062,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Clob(None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3071,6 +3075,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Clob(Some(50)),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3083,6 +3088,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Binary(Some(50)),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3095,6 +3101,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3107,6 +3114,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Blob(None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3119,6 +3127,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::Blob(Some(50)),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3131,6 +3140,7 @@ fn parse_cast() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("details"))),
data_type: DataType::JSONB,
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand All @@ -3146,6 +3156,7 @@ fn parse_try_cast() {
kind: CastKind::TryCast,
expr: Box::new(Expr::Identifier(Ident::new("id"))),
data_type: DataType::BigInt(None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand Down Expand Up @@ -6446,6 +6457,7 @@ fn interval_disallow_interval_expr_double_colon() {
fractional_seconds_precision: None,
})),
data_type: DataType::Text,
array: false,
format: None,
}
)
Expand Down Expand Up @@ -9161,6 +9173,7 @@ fn parse_double_colon_cast_at_timezone() {
.with_empty_span()
)),
data_type: DataType::Timestamp(None, TimezoneInfo::None),
array: false,
format: None
}),
time_zone: Box::new(Expr::Value(
Expand Down Expand Up @@ -13293,6 +13306,7 @@ fn test_dictionary_syntax() {
(Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(),
)),
data_type: DataType::Timestamp(None, TimezoneInfo::None),
array: false,
format: None,
}),
},
Expand All @@ -13304,6 +13318,7 @@ fn test_dictionary_syntax() {
(Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(),
)),
data_type: DataType::Timestamp(None, TimezoneInfo::None),
array: false,
format: None,
}),
},
Expand Down Expand Up @@ -13547,6 +13562,7 @@ fn test_extract_seconds_ok() {
fields: None,
precision: None
},
array: false,
format: None,
}),
}
Expand Down Expand Up @@ -13575,6 +13591,7 @@ fn test_extract_seconds_ok() {
fields: None,
precision: None,
},
array: false,
format: None,
}),
})],
Expand Down Expand Up @@ -13632,6 +13649,7 @@ fn test_extract_seconds_single_quote_ok() {
fields: None,
precision: None
},
array: false,
format: None,
}),
}
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_databricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ fn data_type_timestamp_ntz() {
"created_at".into()
)))),
data_type: DataType::TimestampNtz(None),
array: false,
format: None
}
);
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ fn test_duckdb_specific_int_types() {
Value::Number("123".parse().unwrap(), false).with_empty_span()
)),
data_type: data_type.clone(),
array: false,
format: None,
},
expr_from_projection(&select.projection[0])
Expand Down
27 changes: 27 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,25 @@ fn test_functional_key_part() {
)),
}),
data_type: DataType::Unsigned,
array: false,
format: None,
})),
);
assert_eq!(
index_column(mysql_and_generic().verified_stmt(
r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.fields' AS UNSIGNED ARRAY)) ASC))"#
)),
Expr::Nested(Box::new(Expr::Cast {
kind: CastKind::Cast,
expr: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Identifier(Ident::new("col"))),
op: BinaryOperator::LongArrow,
right: Box::new(Expr::Value(
Value::SingleQuotedString("$.fields".to_string()).with_empty_span()
)),
}),
data_type: DataType::Unsigned,
array: true,
format: None,
})),
);
Expand Down Expand Up @@ -4096,6 +4115,14 @@ fn parse_cast_integers() {
.expect_err("CAST doesn't allow display width");
}

#[test]
fn parse_cast_array() {
mysql().verified_expr("CAST(foo AS SIGNED ARRAY)");
mysql()
.run_parser_method("CAST(foo AS ARRAY)", |p| p.parse_expr())
.expect_err("ARRAY alone is not a type");
}

#[test]
fn parse_match_against_with_alias() {
let sql = "SELECT tbl.ProjectID FROM surveys.tbl1 AS tbl WHERE MATCH (tbl.ReferenceID) AGAINST ('AAA' IN BOOLEAN MODE)";
Expand Down
5 changes: 5 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,7 @@ fn parse_execute() {
(Value::Number("1337".parse().unwrap(), false)).with_empty_span()
)),
data_type: DataType::SmallInt(None),
array: false,
format: None
},
alias: None
Expand All @@ -1717,6 +1718,7 @@ fn parse_execute() {
(Value::Number("7331".parse().unwrap(), false)).with_empty_span()
)),
data_type: DataType::SmallInt(None),
array: false,
format: None
},
alias: None
Expand Down Expand Up @@ -2343,6 +2345,7 @@ fn parse_array_index_expr() {
))),
None
)),
array: false,
format: None,
}))),
access_chain: vec![
Expand Down Expand Up @@ -5570,6 +5573,7 @@ fn parse_at_time_zone() {
Value::SingleQuotedString("America/Los_Angeles".to_owned()).with_empty_span(),
)),
data_type: DataType::Text,
array: false,
format: None,
}),
}),
Expand Down Expand Up @@ -6386,6 +6390,7 @@ fn arrow_cast_precedence() {
(Value::SingleQuotedString("bar".to_string())).with_empty_span()
)),
data_type: DataType::Text,
array: false,
format: None,
}),
}
Expand Down
12 changes: 7 additions & 5 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1101,8 +1101,8 @@ fn parse_create_dynamic_table() {
" EXTERNAL_VOLUME='my_external_volume'",
" CATALOG='SNOWFLAKE'",
" BASE_LOCATION='my_iceberg_table'",
" TARGET_LAG='20 minutes'",
" WAREHOUSE=mywh",
" TARGET_LAG='20 minutes'",
" WAREHOUSE=mywh",
" AS SELECT product_id, product_name FROM staging_table"
));

Expand Down Expand Up @@ -1250,6 +1250,7 @@ fn parse_array() {
kind: CastKind::Cast,
expr: Box::new(Expr::Identifier(Ident::new("a"))),
data_type: DataType::Array(ArrayElemTypeDef::None),
array: false,
format: None,
},
expr_from_projection(only(&select.projection))
Expand Down Expand Up @@ -1460,8 +1461,6 @@ fn parse_semi_structured_data_traversal() {
Expr::JsonAccess {
value: Box::new(Expr::Cast {
kind: CastKind::DoubleColon,
data_type: DataType::Array(ArrayElemTypeDef::None),
format: None,
expr: Box::new(Expr::JsonAccess {
value: Box::new(Expr::Identifier(Ident::new("a"))),
path: JsonPath {
Expand All @@ -1470,7 +1469,10 @@ fn parse_semi_structured_data_traversal() {
quoted: false
}]
}
})
}),
data_type: DataType::Array(ArrayElemTypeDef::None),
array: false,
format: None,
}),
path: JsonPath {
path: vec![JsonPathElem::Bracket {
Expand Down