Skip to content

Commit 89bcbe7

Browse files
Merge branch 'main' into sqlite-pragma-boolean-values
2 parents 582c4e4 + eb61422 commit 89bcbe7

20 files changed

Lines changed: 498 additions & 77 deletions

‎.github/workflows/rust.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ jobs:
8181
uses: ./.github/actions/setup-builder
8282
with:
8383
targets: 'thumbv6m-none-eabi'
84+
- run: cargo test --release --no-default-features --test no_std_recursion
8485
- run: cargo check --no-default-features --target thumbv6m-none-eabi
8586
- run: cargo check --no-default-features --features visitor --target thumbv6m-none-eabi
8687

‎src/ast/ddl.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4386,6 +4386,7 @@ pub struct CreateView {
43864386
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
43874387
pub secure: bool,
43884388
/// View name
4389+
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
43894390
pub name: ObjectName,
43904391
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
43914392
/// Example:

‎src/ast/dml.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,13 @@ pub enum MergeAction {
608608
/// The `DELETE` token that starts the sub-expression.
609609
delete_token: AttachedToken,
610610
},
611+
/// A `DO NOTHING` clause.
612+
DoNothing {
613+
/// The `DO` token that starts the sub-expression.
614+
do_token: AttachedToken,
615+
/// The `NOTHING` token that ends the sub-expression.
616+
nothing_token: AttachedToken,
617+
},
611618
}
612619

613620
impl Display for MergeAction {
@@ -622,6 +629,9 @@ impl Display for MergeAction {
622629
MergeAction::Delete { .. } => {
623630
write!(f, "DELETE")
624631
}
632+
MergeAction::DoNothing { .. } => {
633+
write!(f, "DO NOTHING")
634+
}
625635
}
626636
}
627637
}

‎src/ast/mod.rs‎

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ pub enum Expr {
10271027
/// Pattern expression.
10281028
pattern: Box<Expr>,
10291029
/// Optional escape character.
1030-
escape_char: Option<ValueWithSpan>,
1030+
escape_char: Option<Box<Expr>>,
10311031
},
10321032
/// `ILIKE` (case-insensitive `LIKE`)
10331033
ILike {
@@ -1041,7 +1041,7 @@ pub enum Expr {
10411041
/// Pattern expression.
10421042
pattern: Box<Expr>,
10431043
/// Optional escape character.
1044-
escape_char: Option<ValueWithSpan>,
1044+
escape_char: Option<Box<Expr>>,
10451045
},
10461046
/// `SIMILAR TO` regex
10471047
SimilarTo {
@@ -1052,7 +1052,7 @@ pub enum Expr {
10521052
/// Pattern expression.
10531053
pattern: Box<Expr>,
10541054
/// Optional escape character.
1055-
escape_char: Option<ValueWithSpan>,
1055+
escape_char: Option<Box<Expr>>,
10561056
},
10571057
/// MySQL: `RLIKE` regex or `REGEXP` regex
10581058
RLike {
@@ -1487,6 +1487,9 @@ pub enum AccessExpr {
14871487
impl fmt::Display for AccessExpr {
14881488
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14891489
match self {
1490+
AccessExpr::Dot(Expr::Value(value)) if matches!(value.value, Value::Number(_, _)) => {
1491+
write!(f, " . {value}")
1492+
}
14901493
AccessExpr::Dot(expr) => write!(f, ".{expr}"),
14911494
AccessExpr::Subscript(subscript) => write!(f, "[{subscript}]"),
14921495
}
@@ -7544,34 +7547,22 @@ pub struct Grantee {
75447547

75457548
impl fmt::Display for Grantee {
75467549
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7547-
match self.grantee_type {
7548-
GranteesType::Role => {
7549-
write!(f, "ROLE ")?;
7550-
}
7551-
GranteesType::Share => {
7552-
write!(f, "SHARE ")?;
7553-
}
7554-
GranteesType::User => {
7555-
write!(f, "USER ")?;
7556-
}
7557-
GranteesType::Group => {
7558-
write!(f, "GROUP ")?;
7559-
}
7560-
GranteesType::Public => {
7561-
write!(f, "PUBLIC ")?;
7562-
}
7563-
GranteesType::DatabaseRole => {
7564-
write!(f, "DATABASE ROLE ")?;
7565-
}
7566-
GranteesType::Application => {
7567-
write!(f, "APPLICATION ")?;
7568-
}
7569-
GranteesType::ApplicationRole => {
7570-
write!(f, "APPLICATION ROLE ")?;
7550+
let keyword = match self.grantee_type {
7551+
GranteesType::Role => "ROLE",
7552+
GranteesType::Share => "SHARE",
7553+
GranteesType::User => "USER",
7554+
GranteesType::Group => "GROUP",
7555+
GranteesType::Public => "PUBLIC",
7556+
GranteesType::DatabaseRole => "DATABASE ROLE",
7557+
GranteesType::Application => "APPLICATION",
7558+
GranteesType::ApplicationRole => "APPLICATION ROLE",
7559+
GranteesType::None => "",
7560+
};
7561+
f.write_str(keyword)?;
7562+
if let Some(name) = &self.name {
7563+
if !keyword.is_empty() {
7564+
f.write_str(" ")?;
75717565
}
7572-
GranteesType::None => (),
7573-
}
7574-
if let Some(ref name) = self.name {
75757566
name.fmt(f)?;
75767567
}
75777568
Ok(())

‎src/ast/query.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ impl fmt::Display for TableFactor {
22312231
json_path.fmt(f)?;
22322232
}
22332233
if !partitions.is_empty() {
2234-
write!(f, "PARTITION ({})", display_comma_separated(partitions))?;
2234+
write!(f, " PARTITION ({})", display_comma_separated(partitions))?;
22352235
}
22362236
if let Some(args) = args {
22372237
write!(f, "(")?;

‎src/ast/spans.rs‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,6 +2557,10 @@ impl Spanned for MergeAction {
25572557
MergeAction::Insert(expr) => expr.span(),
25582558
MergeAction::Update(expr) => expr.span(),
25592559
MergeAction::Delete { delete_token } => delete_token.0.span,
2560+
MergeAction::DoNothing {
2561+
do_token,
2562+
nothing_token,
2563+
} => do_token.0.span.union(&nothing_token.0.span),
25602564
}
25612565
}
25622566
}
@@ -2909,6 +2913,7 @@ WHERE id = 1
29092913
29102914
WHEN MATCHED AND target_table.x != 'X' THEN DELETE
29112915
WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW
2916+
WHEN MATCHED THEN DO NOTHING
29122917
"#;
29132918

29142919
let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap();
@@ -2917,7 +2922,7 @@ WHERE id = 1
29172922
// ~ assert the span of the whole statement
29182923
let stmt_span = r[0].span();
29192924
assert_eq!(stmt_span.start, (4, 9).into());
2920-
assert_eq!(stmt_span.end, (16, 67).into());
2925+
assert_eq!(stmt_span.end, (17, 37).into());
29212926

29222927
// ~ individual tokens within the statement
29232928
let Statement::Merge(Merge {
@@ -2937,7 +2942,7 @@ WHERE id = 1
29372942
merge_token.0.span,
29382943
Span::new(Location::new(4, 9), Location::new(4, 14))
29392944
);
2940-
assert_eq!(clauses.len(), 4);
2945+
assert_eq!(clauses.len(), 5);
29412946

29422947
// ~ the INSERT clause's TOKENs
29432948
assert_eq!(
@@ -3019,6 +3024,31 @@ WHERE id = 1
30193024
panic!("not a MERGE INSERT clause");
30203025
}
30213026

3027+
assert_eq!(
3028+
clauses[4].when_token.0.span,
3029+
Span::new(Location::new(17, 9), Location::new(17, 13))
3030+
);
3031+
if let MergeAction::DoNothing {
3032+
do_token,
3033+
nothing_token,
3034+
} = &clauses[4].action
3035+
{
3036+
assert_eq!(
3037+
do_token.0.span,
3038+
Span::new(Location::new(17, 27), Location::new(17, 29))
3039+
);
3040+
assert_eq!(
3041+
nothing_token.0.span,
3042+
Span::new(Location::new(17, 30), Location::new(17, 37))
3043+
);
3044+
assert_eq!(
3045+
clauses[4].action.span(),
3046+
Span::new(Location::new(17, 27), Location::new(17, 37))
3047+
);
3048+
} else {
3049+
panic!("not a MERGE DO NOTHING clause");
3050+
}
3051+
30223052
assert!(output.is_none());
30233053
}
30243054

‎src/ast/visitor.rs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,27 @@ mod tests {
12421242
do_visit("SELECT a, b FROM t", &mut visitor);
12431243
assert_eq!(visitor.idents, vec!["a", "b", "t"]);
12441244
}
1245+
1246+
#[derive(Default)]
1247+
struct RelationVisitor {
1248+
relations: Vec<String>,
1249+
}
1250+
1251+
impl Visitor for RelationVisitor {
1252+
type Break = ();
1253+
1254+
fn pre_visit_relation(&mut self, relation: &ObjectName) -> ControlFlow<Self::Break> {
1255+
self.relations.push(relation.to_string());
1256+
ControlFlow::Continue(())
1257+
}
1258+
}
1259+
1260+
#[test]
1261+
fn test_visit_create_view_name_as_relation() {
1262+
let mut visitor = RelationVisitor::default();
1263+
do_visit("CREATE VIEW db1.v AS SELECT * FROM t", &mut visitor);
1264+
assert_eq!(visitor.relations, vec!["db1.v", "t"]);
1265+
}
12451266
}
12461267

12471268
#[cfg(test)]

‎src/dialect/databricks.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,24 @@ impl Dialect for DatabricksDialect {
113113
fn supports_select_item_multi_column_alias(&self) -> bool {
114114
true
115115
}
116+
117+
fn supports_map_literal_with_angle_brackets(&self) -> bool {
118+
true
119+
}
120+
121+
fn supports_string_literal_backslash_escape(&self) -> bool {
122+
true
123+
}
124+
125+
fn supports_select_wildcard_replace(&self) -> bool {
126+
true
127+
}
128+
129+
fn supports_from_first_select(&self) -> bool {
130+
true
131+
}
132+
133+
fn supports_pipe_operator(&self) -> bool {
134+
true
135+
}
116136
}

‎src/dialect/mysql.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ impl Dialect for MySqlDialect {
9898
&self,
9999
parser: &mut crate::parser::Parser,
100100
expr: &crate::ast::Expr,
101-
_precedence: u8,
101+
precedence: u8,
102102
) -> Option<Result<crate::ast::Expr, ParserError>> {
103103
// Parse DIV as an operator
104104
if parser.parse_keyword(Keyword::DIV) {
105105
let left = Box::new(expr.clone());
106-
let right = Box::new(match parser.parse_expr() {
106+
let right = Box::new(match parser.parse_subexpr(precedence) {
107107
Ok(expr) => expr,
108108
Err(e) => return Some(Err(e)),
109109
});

‎src/dialect/postgresql.rs‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,21 @@ use crate::keywords::Keyword;
3333
use crate::parser::{Parser, ParserError};
3434
use crate::tokenizer::Token;
3535

36-
use super::keywords::RESERVED_FOR_IDENTIFIER;
36+
use super::keywords::{self, RESERVED_FOR_IDENTIFIER};
37+
38+
/// Keywords in [`keywords::RESERVED_FOR_TABLE_ALIAS`] because of other dialects, yet are safe for aliasing in PostgreSQL.
39+
/// See <https://www.postgresql.org/docs/current/sql-keywords-appendix.html>.
40+
const RESERVED_EXCLUSIONS_FOR_TABLE_ALIAS: &[Keyword] = &[
41+
Keyword::CLUSTER,
42+
Keyword::DISTRIBUTE,
43+
Keyword::EXPLAIN,
44+
Keyword::MINUS,
45+
Keyword::SAMPLE,
46+
Keyword::SORT,
47+
Keyword::START,
48+
Keyword::TOP,
49+
Keyword::VIEW,
50+
];
3751

3852
/// A [`Dialect`] for [PostgreSQL](https://www.postgresql.org/)
3953
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -91,6 +105,11 @@ impl Dialect for PostgreSqlDialect {
91105
}
92106
}
93107

108+
fn is_table_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
109+
!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw)
110+
|| RESERVED_EXCLUSIONS_FOR_TABLE_ALIAS.contains(kw)
111+
}
112+
94113
/// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
95114
fn is_custom_operator_part(&self, ch: char) -> bool {
96115
matches!(

0 commit comments

Comments
 (0)