Skip to content

Commit 5d1d6bb

Browse files
Merge branch 'main' into fix-join-on-required
2 parents a3244ea + 525d178 commit 5d1d6bb

File tree

8 files changed

+467
-352
lines changed

8 files changed

+467
-352
lines changed

src/dialect/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,12 @@ pub trait Dialect: Debug + Any {
681681
fn supports_partiql(&self) -> bool {
682682
false
683683
}
684+
685+
/// Returns true if the specified keyword is reserved and cannot be
686+
/// used as an identifier without special handling like quoting.
687+
fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
688+
keywords::RESERVED_FOR_IDENTIFIER.contains(&kw)
689+
}
684690
}
685691

686692
/// This represents the operators for which precedence must be defined

src/dialect/postgresql.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,26 @@ impl Dialect for PostgreSqlDialect {
211211
fn supports_load_extension(&self) -> bool {
212212
true
213213
}
214+
215+
/// See <https://www.postgresql.org/docs/current/functions-json.html>
216+
///
217+
/// Required to support the colon in:
218+
/// ```sql
219+
/// SELECT json_object('a': 'b')
220+
/// ```
221+
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
222+
true
223+
}
224+
225+
/// See <https://www.postgresql.org/docs/current/functions-json.html>
226+
///
227+
/// Required to support the label in:
228+
/// ```sql
229+
/// SELECT json_object('label': 'value')
230+
/// ```
231+
fn supports_named_fn_args_with_expr_name(&self) -> bool {
232+
true
233+
}
214234
}
215235

216236
pub fn parse_create(parser: &mut Parser) -> Option<Result<Statement, ParserError>> {

src/dialect/snowflake.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ use alloc::vec::Vec;
3838
#[cfg(not(feature = "std"))]
3939
use alloc::{format, vec};
4040

41+
use super::keywords::RESERVED_FOR_IDENTIFIER;
42+
4143
/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
4244
#[derive(Debug, Default)]
4345
pub struct SnowflakeDialect;
@@ -214,6 +216,16 @@ impl Dialect for SnowflakeDialect {
214216
fn supports_show_like_before_in(&self) -> bool {
215217
true
216218
}
219+
220+
fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
221+
// Unreserve some keywords that Snowflake accepts as identifiers
222+
// See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
223+
if matches!(kw, Keyword::INTERVAL) {
224+
false
225+
} else {
226+
RESERVED_FOR_IDENTIFIER.contains(&kw)
227+
}
228+
}
217229
}
218230

219231
/// Parse snowflake create table statement.

src/keywords.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,3 +948,13 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
948948
Keyword::INTO,
949949
Keyword::END,
950950
];
951+
952+
/// Global list of reserved keywords that cannot be parsed as identifiers
953+
/// without special handling like quoting. Parser should call `Dialect::is_reserved_for_identifier`
954+
/// to allow for each dialect to customize the list.
955+
pub const RESERVED_FOR_IDENTIFIER: &[Keyword] = &[
956+
Keyword::EXISTS,
957+
Keyword::INTERVAL,
958+
Keyword::STRUCT,
959+
Keyword::TRIM,
960+
];

src/parser/mod.rs

Lines changed: 226 additions & 173 deletions
Large diffs are not rendered by default.

tests/sqlparser_common.rs

Lines changed: 189 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use sqlparser::dialect::{
3434
GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect,
3535
SQLiteDialect, SnowflakeDialect,
3636
};
37-
use sqlparser::keywords::ALL_KEYWORDS;
37+
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
3838
use sqlparser::parser::{Parser, ParserError, ParserOptions};
3939
use sqlparser::tokenizer::Tokenizer;
4040
use test_utils::{
@@ -1471,6 +1471,173 @@ fn parse_json_ops_without_colon() {
14711471
}
14721472
}
14731473

1474+
#[test]
1475+
fn parse_json_object() {
1476+
let dialects = TestedDialects::new(vec![
1477+
Box::new(MsSqlDialect {}),
1478+
Box::new(PostgreSqlDialect {}),
1479+
]);
1480+
let select = dialects.verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : 1)");
1481+
match expr_from_projection(&select.projection[0]) {
1482+
Expr::Function(Function {
1483+
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
1484+
..
1485+
}) => assert_eq!(
1486+
&[
1487+
FunctionArg::ExprNamed {
1488+
name: Expr::Value(Value::SingleQuotedString("name".into())),
1489+
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
1490+
"value".into()
1491+
))),
1492+
operator: FunctionArgOperator::Colon
1493+
},
1494+
FunctionArg::ExprNamed {
1495+
name: Expr::Value(Value::SingleQuotedString("type".into())),
1496+
arg: FunctionArgExpr::Expr(Expr::Value(number("1"))),
1497+
operator: FunctionArgOperator::Colon
1498+
}
1499+
],
1500+
&args[..]
1501+
),
1502+
_ => unreachable!(),
1503+
}
1504+
let select = dialects
1505+
.verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : NULL ABSENT ON NULL)");
1506+
match expr_from_projection(&select.projection[0]) {
1507+
Expr::Function(Function {
1508+
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
1509+
..
1510+
}) => {
1511+
assert_eq!(
1512+
&[
1513+
FunctionArg::ExprNamed {
1514+
name: Expr::Value(Value::SingleQuotedString("name".into())),
1515+
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
1516+
"value".into()
1517+
))),
1518+
operator: FunctionArgOperator::Colon
1519+
},
1520+
FunctionArg::ExprNamed {
1521+
name: Expr::Value(Value::SingleQuotedString("type".into())),
1522+
arg: FunctionArgExpr::Expr(Expr::Value(Value::Null)),
1523+
operator: FunctionArgOperator::Colon
1524+
}
1525+
],
1526+
&args[..]
1527+
);
1528+
assert_eq!(
1529+
&[FunctionArgumentClause::JsonNullClause(
1530+
JsonNullClause::AbsentOnNull
1531+
)],
1532+
&clauses[..]
1533+
);
1534+
}
1535+
_ => unreachable!(),
1536+
}
1537+
let select = dialects.verified_only_select("SELECT JSON_OBJECT(NULL ON NULL)");
1538+
match expr_from_projection(&select.projection[0]) {
1539+
Expr::Function(Function {
1540+
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
1541+
..
1542+
}) => {
1543+
assert!(args.is_empty());
1544+
assert_eq!(
1545+
&[FunctionArgumentClause::JsonNullClause(
1546+
JsonNullClause::NullOnNull
1547+
)],
1548+
&clauses[..]
1549+
);
1550+
}
1551+
_ => unreachable!(),
1552+
}
1553+
let select = dialects.verified_only_select("SELECT JSON_OBJECT(ABSENT ON NULL)");
1554+
match expr_from_projection(&select.projection[0]) {
1555+
Expr::Function(Function {
1556+
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
1557+
..
1558+
}) => {
1559+
assert!(args.is_empty());
1560+
assert_eq!(
1561+
&[FunctionArgumentClause::JsonNullClause(
1562+
JsonNullClause::AbsentOnNull
1563+
)],
1564+
&clauses[..]
1565+
);
1566+
}
1567+
_ => unreachable!(),
1568+
}
1569+
let select = dialects.verified_only_select(
1570+
"SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_ARRAY(1, 2) ABSENT ON NULL)",
1571+
);
1572+
match expr_from_projection(&select.projection[0]) {
1573+
Expr::Function(Function {
1574+
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
1575+
..
1576+
}) => {
1577+
assert_eq!(
1578+
&FunctionArg::ExprNamed {
1579+
name: Expr::Value(Value::SingleQuotedString("name".into())),
1580+
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
1581+
"value".into()
1582+
))),
1583+
operator: FunctionArgOperator::Colon
1584+
},
1585+
&args[0]
1586+
);
1587+
assert!(matches!(
1588+
args[1],
1589+
FunctionArg::ExprNamed {
1590+
name: Expr::Value(Value::SingleQuotedString(_)),
1591+
arg: FunctionArgExpr::Expr(Expr::Function(_)),
1592+
operator: FunctionArgOperator::Colon
1593+
}
1594+
));
1595+
assert_eq!(
1596+
&[FunctionArgumentClause::JsonNullClause(
1597+
JsonNullClause::AbsentOnNull
1598+
)],
1599+
&clauses[..]
1600+
);
1601+
}
1602+
_ => unreachable!(),
1603+
}
1604+
let select = dialects.verified_only_select(
1605+
"SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_OBJECT('type_id' : 1, 'name' : 'a') NULL ON NULL)",
1606+
);
1607+
match expr_from_projection(&select.projection[0]) {
1608+
Expr::Function(Function {
1609+
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
1610+
..
1611+
}) => {
1612+
assert_eq!(
1613+
&FunctionArg::ExprNamed {
1614+
name: Expr::Value(Value::SingleQuotedString("name".into())),
1615+
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
1616+
"value".into()
1617+
))),
1618+
operator: FunctionArgOperator::Colon
1619+
},
1620+
&args[0]
1621+
);
1622+
assert!(matches!(
1623+
args[1],
1624+
FunctionArg::ExprNamed {
1625+
name: Expr::Value(Value::SingleQuotedString(_)),
1626+
arg: FunctionArgExpr::Expr(Expr::Function(_)),
1627+
operator: FunctionArgOperator::Colon
1628+
}
1629+
));
1630+
assert_eq!(
1631+
&[FunctionArgumentClause::JsonNullClause(
1632+
JsonNullClause::NullOnNull
1633+
)],
1634+
&clauses[..]
1635+
);
1636+
}
1637+
_ => unreachable!(),
1638+
}
1639+
}
1640+
14741641
#[test]
14751642
fn parse_mod_no_spaces() {
14761643
use self::Expr::*;
@@ -4416,7 +4583,10 @@ fn parse_explain_query_plan() {
44164583

44174584
#[test]
44184585
fn parse_named_argument_function() {
4419-
let dialects = all_dialects_where(|d| d.supports_named_fn_args_with_rarrow_operator());
4586+
let dialects = all_dialects_where(|d| {
4587+
d.supports_named_fn_args_with_rarrow_operator()
4588+
&& !d.supports_named_fn_args_with_expr_name()
4589+
});
44204590
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";
44214591
let select = dialects.verified_only_select(sql);
44224592

@@ -5113,7 +5283,6 @@ fn parse_interval_dont_require_unit() {
51135283
#[test]
51145284
fn parse_interval_require_unit() {
51155285
let dialects = all_dialects_where(|d| d.require_interval_qualifier());
5116-
51175286
let sql = "SELECT INTERVAL '1 DAY'";
51185287
let err = dialects.parse_sql_statements(sql).unwrap_err();
51195288
assert_eq!(
@@ -12240,5 +12409,21 @@ fn parse_no_condition_join_strategy() {
1224012409
)
1224112410
);
1224212411
}
12243-
}
12412+
}
12413+
12414+
fn test_reserved_keywords_for_identifiers() {
12415+
let dialects = all_dialects_where(|d| d.is_reserved_for_identifier(Keyword::INTERVAL));
12416+
// Dialects that reserve the word INTERVAL will not allow it as an unquoted identifier
12417+
let sql = "SELECT MAX(interval) FROM tbl";
12418+
assert_eq!(
12419+
dialects.parse_sql_statements(sql),
12420+
Err(ParserError::ParserError(
12421+
"Expected: an expression, found: )".to_string()
12422+
))
12423+
);
12424+
12425+
// Dialects that do not reserve the word INTERVAL will allow it
12426+
let dialects = all_dialects_where(|d| !d.is_reserved_for_identifier(Keyword::INTERVAL));
12427+
let sql = "SELECT MAX(interval) FROM tbl";
12428+
dialects.parse_sql_statements(sql).unwrap();
1224412429
}

0 commit comments

Comments
 (0)