Skip to content

Commit 0dc41fa

Browse files
feat(parser): add IS [NOT] JSON predicate support
Introduce AST and parser support for IS JSON predicates, including optional VALUE/SCALAR/ARRAY/OBJECT and WITH/WITHOUT UNIQUE [KEYS] modifiers. Gate parsing by dialect capability, enable Generic/ANSI/PostgreSQL/Oracle, update IS diagnostic hints accordingly, and include parser-side regression coverage. Also apply parser-only control-flow cleanups to keep strict clippy (-D warnings) green.
1 parent 83baf5e commit 0dc41fa

File tree

9 files changed

+263
-96
lines changed

9 files changed

+263
-96
lines changed

src/ast/mod.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,17 @@ pub enum Expr {
876876
IsDistinctFrom(Box<Expr>, Box<Expr>),
877877
/// `IS NOT DISTINCT FROM` operator
878878
IsNotDistinctFrom(Box<Expr>, Box<Expr>),
879+
/// `<expr> IS [NOT] JSON [VALUE|SCALAR|ARRAY|OBJECT] [WITH|WITHOUT UNIQUE [KEYS]]`
880+
IsJson {
881+
/// Expression being tested.
882+
expr: Box<Expr>,
883+
/// Optional JSON shape constraint.
884+
kind: Option<JsonPredicateType>,
885+
/// Optional duplicate-key handling constraint for JSON objects.
886+
unique_keys: Option<JsonKeyUniqueness>,
887+
/// `true` when `NOT` is present.
888+
negated: bool,
889+
},
879890
/// `<expr> IS [ NOT ] [ form ] NORMALIZED`
880891
IsNormalized {
881892
/// Expression being tested.
@@ -1685,6 +1696,25 @@ impl fmt::Display for Expr {
16851696
Expr::IsNotNull(ast) => write!(f, "{ast} IS NOT NULL"),
16861697
Expr::IsUnknown(ast) => write!(f, "{ast} IS UNKNOWN"),
16871698
Expr::IsNotUnknown(ast) => write!(f, "{ast} IS NOT UNKNOWN"),
1699+
Expr::IsJson {
1700+
expr,
1701+
kind,
1702+
unique_keys,
1703+
negated,
1704+
} => {
1705+
write!(f, "{expr} IS ")?;
1706+
if *negated {
1707+
write!(f, "NOT ")?;
1708+
}
1709+
write!(f, "JSON")?;
1710+
if let Some(kind) = kind {
1711+
write!(f, " {kind}")?;
1712+
}
1713+
if let Some(unique_keys) = unique_keys {
1714+
write!(f, " {unique_keys}")?;
1715+
}
1716+
Ok(())
1717+
}
16881718
Expr::InList {
16891719
expr,
16901720
list,
@@ -8107,6 +8137,52 @@ pub enum AnalyzeFormat {
81078137
TREE,
81088138
}
81098139

8140+
/// Optional type constraint for `IS JSON`.
8141+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
8142+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8143+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8144+
pub enum JsonPredicateType {
8145+
/// `VALUE` form.
8146+
Value,
8147+
/// `SCALAR` form.
8148+
Scalar,
8149+
/// `ARRAY` form.
8150+
Array,
8151+
/// `OBJECT` form.
8152+
Object,
8153+
}
8154+
8155+
impl fmt::Display for JsonPredicateType {
8156+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8157+
match self {
8158+
JsonPredicateType::Value => write!(f, "VALUE"),
8159+
JsonPredicateType::Scalar => write!(f, "SCALAR"),
8160+
JsonPredicateType::Array => write!(f, "ARRAY"),
8161+
JsonPredicateType::Object => write!(f, "OBJECT"),
8162+
}
8163+
}
8164+
}
8165+
8166+
/// Optional duplicate-key handling for `IS JSON`.
8167+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
8168+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8169+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8170+
pub enum JsonKeyUniqueness {
8171+
/// `WITH UNIQUE KEYS` form.
8172+
WithUniqueKeys,
8173+
/// `WITHOUT UNIQUE KEYS` form.
8174+
WithoutUniqueKeys,
8175+
}
8176+
8177+
impl fmt::Display for JsonKeyUniqueness {
8178+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8179+
match self {
8180+
JsonKeyUniqueness::WithUniqueKeys => write!(f, "WITH UNIQUE KEYS"),
8181+
JsonKeyUniqueness::WithoutUniqueKeys => write!(f, "WITHOUT UNIQUE KEYS"),
8182+
}
8183+
}
8184+
}
8185+
81108186
impl fmt::Display for AnalyzeFormat {
81118187
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81128188
f.write_str(match self {

src/ast/spans.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,12 @@ impl Spanned for Expr {
14451445
Expr::IsNotNull(expr) => expr.span(),
14461446
Expr::IsUnknown(expr) => expr.span(),
14471447
Expr::IsNotUnknown(expr) => expr.span(),
1448+
Expr::IsJson {
1449+
expr,
1450+
kind: _,
1451+
unique_keys: _,
1452+
negated: _,
1453+
} => expr.span(),
14481454
Expr::IsDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()),
14491455
Expr::IsNotDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()),
14501456
Expr::InList {

src/dialect/ansi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ impl Dialect for AnsiDialect {
3939
fn supports_nested_comments(&self) -> bool {
4040
true
4141
}
42+
43+
fn supports_is_json_predicate(&self) -> bool {
44+
true
45+
}
4246
}

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,8 @@ impl Dialect for GenericDialect {
284284
fn supports_key_column_option(&self) -> bool {
285285
true
286286
}
287+
288+
fn supports_is_json_predicate(&self) -> bool {
289+
true
290+
}
287291
}

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,11 @@ pub trait Dialect: Debug + Any {
13991399
false
14001400
}
14011401

1402+
/// Returns true if the dialect supports the `IS [NOT] JSON` predicate.
1403+
fn supports_is_json_predicate(&self) -> bool {
1404+
false
1405+
}
1406+
14021407
/// Returns true if this dialect allows an optional `SIGNED` suffix after integer data types.
14031408
///
14041409
/// Example:

src/dialect/oracle.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,8 @@ impl Dialect for OracleDialect {
114114
fn supports_insert_table_alias(&self) -> bool {
115115
true
116116
}
117+
118+
fn supports_is_json_predicate(&self) -> bool {
119+
true
120+
}
117121
}

src/dialect/postgresql.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,8 @@ impl Dialect for PostgreSqlDialect {
306306
fn supports_create_table_like_parenthesized(&self) -> bool {
307307
true
308308
}
309+
310+
fn supports_is_json_predicate(&self) -> bool {
311+
true
312+
}
309313
}

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,7 @@ define_keywords!(
902902
SAFE_CAST,
903903
SAMPLE,
904904
SAVEPOINT,
905+
SCALAR,
905906
SCHEMA,
906907
SCHEMAS,
907908
SCOPE,

0 commit comments

Comments
 (0)